From 079a176d87a4da4cb18864c54d3932131e11e229 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 29 Sep 2010 17:31:29 +0900 Subject: spi: omap2_mcspi: make use of dev_vdbg() dev_vdbg() is only compiled when VERBOSE is defined, so there's no need to wrap dev_dbg() on #ifdef VERBOSE .. #endif as we can use dev_vdbg() directly. Signed-off-by: Felipe Balbi Signed-off-by: Grant Likely --- drivers/spi/omap2_mcspi.c | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index b3a94ca0a75a..d70392795055 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -489,10 +489,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_err(&spi->dev, "TXS timed out\n"); goto out; } -#ifdef VERBOSE - dev_dbg(&spi->dev, "write-%d %02x\n", + dev_vdbg(&spi->dev, "write-%d %02x\n", word_len, *tx); -#endif __raw_writel(*tx++, tx_reg); } if (rx != NULL) { @@ -506,10 +504,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) (l & OMAP2_MCSPI_CHCONF_TURBO)) { omap2_mcspi_set_enable(spi, 0); *rx++ = __raw_readl(rx_reg); -#ifdef VERBOSE - dev_dbg(&spi->dev, "read-%d %02x\n", + dev_vdbg(&spi->dev, "read-%d %02x\n", word_len, *(rx - 1)); -#endif if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS) < 0) { dev_err(&spi->dev, @@ -522,10 +518,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) } *rx++ = __raw_readl(rx_reg); -#ifdef VERBOSE - dev_dbg(&spi->dev, "read-%d %02x\n", + dev_vdbg(&spi->dev, "read-%d %02x\n", word_len, *(rx - 1)); -#endif } } while (c); } else if (word_len <= 16) { @@ -542,10 +536,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_err(&spi->dev, "TXS timed out\n"); goto out; } -#ifdef VERBOSE - dev_dbg(&spi->dev, "write-%d %04x\n", + dev_vdbg(&spi->dev, "write-%d %04x\n", word_len, *tx); -#endif __raw_writel(*tx++, tx_reg); } if (rx != NULL) { @@ -559,10 +551,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) (l & OMAP2_MCSPI_CHCONF_TURBO)) { omap2_mcspi_set_enable(spi, 0); *rx++ = __raw_readl(rx_reg); -#ifdef VERBOSE - dev_dbg(&spi->dev, "read-%d %04x\n", + dev_vdbg(&spi->dev, "read-%d %04x\n", word_len, *(rx - 1)); -#endif if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS) < 0) { dev_err(&spi->dev, @@ -575,10 +565,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) } *rx++ = __raw_readl(rx_reg); -#ifdef VERBOSE - dev_dbg(&spi->dev, "read-%d %04x\n", + dev_vdbg(&spi->dev, "read-%d %04x\n", word_len, *(rx - 1)); -#endif } } while (c); } else if (word_len <= 32) { @@ -595,10 +583,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_err(&spi->dev, "TXS timed out\n"); goto out; } -#ifdef VERBOSE - dev_dbg(&spi->dev, "write-%d %08x\n", + dev_vdbg(&spi->dev, "write-%d %08x\n", word_len, *tx); -#endif __raw_writel(*tx++, tx_reg); } if (rx != NULL) { @@ -612,10 +598,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) (l & OMAP2_MCSPI_CHCONF_TURBO)) { omap2_mcspi_set_enable(spi, 0); *rx++ = __raw_readl(rx_reg); -#ifdef VERBOSE - dev_dbg(&spi->dev, "read-%d %08x\n", + dev_vdbg(&spi->dev, "read-%d %08x\n", word_len, *(rx - 1)); -#endif if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS) < 0) { dev_err(&spi->dev, @@ -628,10 +612,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) } *rx++ = __raw_readl(rx_reg); -#ifdef VERBOSE - dev_dbg(&spi->dev, "read-%d %08x\n", + dev_vdbg(&spi->dev, "read-%d %08x\n", word_len, *(rx - 1)); -#endif } } while (c); } -- cgit v1.2.2 From e447d3588e1c5944f607083cb509663f8015d420 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 29 Sep 2010 17:31:29 +0900 Subject: spi/orion: Drop unnecessary null test list_for_each_entry binds its first argument to a non-null value, and thus any null test on the value of that argument is superfluous. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ iterator I; expression x,E; @@ I(x,...) { <... - (x != NULL) && E ...> } // Signed-off-by: Julia Lawall Signed-off-by: Grant Likely --- drivers/spi/orion_spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/orion_spi.c b/drivers/spi/orion_spi.c index 3aea50da7b29..0b677dc041ad 100644 --- a/drivers/spi/orion_spi.c +++ b/drivers/spi/orion_spi.c @@ -404,7 +404,7 @@ static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m) goto msg_rejected; } - if ((t != NULL) && t->bits_per_word) + if (t->bits_per_word) bits_per_word = t->bits_per_word; if ((bits_per_word != 8) && (bits_per_word != 16)) { @@ -415,7 +415,7 @@ static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m) goto msg_rejected; } /*make sure buffer length is even when working in 16 bit mode*/ - if ((t != NULL) && (t->bits_per_word == 16) && (t->len & 1)) { + if ((t->bits_per_word == 16) && (t->len & 1)) { dev_err(&spi->dev, "message rejected : " "odd data length (%d) while in 16 bit mode\n", -- cgit v1.2.2 From 4a4fd47155ac49b62de5177a780c245e967752f3 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 29 Sep 2010 17:31:30 +0900 Subject: spi/amba-pl022: Fix error case return statement. The return -EINVAL appears to only make sense if the if branch that it is aligned with is taken, and the indentation indicates that this is the authors intent, so move it into that branch. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r disable braces4@ position p1,p2; statement S1,S2; @@ ( if (...) { ... } | if (...) S1@p1 S2@p2 ) @script:python@ p1 << r.p1; p2 << r.p2; @@ if (p1[0].column == p2[0].column): cocci.print_main("branch",p1) cocci.print_secs("after",p2) // Signed-off-by: Julia Lawall Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 4c37c4e28647..8cdddc97325c 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1350,10 +1350,11 @@ static int verify_controller_parameters(struct pl022 *pl022, if ((chip_info->duplex != SSP_MICROWIRE_CHANNEL_FULL_DUPLEX) && (chip_info->duplex != - SSP_MICROWIRE_CHANNEL_HALF_DUPLEX)) + SSP_MICROWIRE_CHANNEL_HALF_DUPLEX)) { dev_err(chip_info->dev, "Microwire duplex mode is configured incorrectly\n"); return -EINVAL; + } } else { if (chip_info->duplex != SSP_MICROWIRE_CHANNEL_FULL_DUPLEX) dev_err(chip_info->dev, -- cgit v1.2.2 From e02ddd442a532c73e547ae3735c8012e3bd719a5 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Wed, 29 Sep 2010 17:31:31 +0900 Subject: spi/s3c64xx: Prevent unnecessary map-unmap Since we use DMA mode only for xfers bigger than FIFO size, do not map/unmap buffers for polling mode transfers. Signed-off-by: Jassi Brar Signed-off-by: Grant Likely --- drivers/spi/spi_s3c64xx.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c index c3038da2648a..e7b893f2a21b 100644 --- a/drivers/spi/spi_s3c64xx.c +++ b/drivers/spi/spi_s3c64xx.c @@ -499,6 +499,7 @@ static void s3c64xx_spi_dma_txcb(struct s3c2410_dma_chan *chan, void *buf_id, static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, struct spi_message *msg) { + struct s3c64xx_spi_info *sci = sdd->cntrlr_info; struct device *dev = &sdd->pdev->dev; struct spi_transfer *xfer; @@ -514,6 +515,9 @@ static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, /* Map until end or first fail */ list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1)) + continue; + if (xfer->tx_buf != NULL) { xfer->tx_dma = dma_map_single(dev, (void *)xfer->tx_buf, xfer->len, @@ -545,6 +549,7 @@ static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd, struct spi_message *msg) { + struct s3c64xx_spi_info *sci = sdd->cntrlr_info; struct device *dev = &sdd->pdev->dev; struct spi_transfer *xfer; @@ -553,6 +558,9 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd, list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1)) + continue; + if (xfer->rx_buf != NULL && xfer->rx_dma != XFER_DMAADDR_INVALID) dma_unmap_single(dev, xfer->rx_dma, -- cgit v1.2.2 From b42a81ca0fa7b3b442a0731ffc4e7db44464b5f2 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Wed, 29 Sep 2010 17:31:33 +0900 Subject: spi/s3c64xx: Consider the clk_from_cmu flag Newer SoCs have the SPI clock scaling control in platform's clock management unit. Inorder for such SoCs to work, we need to check the flag clk_from_cmu before making any clock changes. Signed-off-by: Jassi Brar Signed-off-by: Grant Likely --- drivers/spi/spi_s3c64xx.c | 94 ++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 38 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c index e7b893f2a21b..9e0aa3c7b4d1 100644 --- a/drivers/spi/spi_s3c64xx.c +++ b/drivers/spi/spi_s3c64xx.c @@ -399,13 +399,18 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) { + struct s3c64xx_spi_info *sci = sdd->cntrlr_info; void __iomem *regs = sdd->regs; u32 val; /* Disable Clock */ - val = readl(regs + S3C64XX_SPI_CLK_CFG); - val &= ~S3C64XX_SPI_ENCLK_ENABLE; - writel(val, regs + S3C64XX_SPI_CLK_CFG); + if (sci->clk_from_cmu) { + clk_disable(sdd->src_clk); + } else { + val = readl(regs + S3C64XX_SPI_CLK_CFG); + val &= ~S3C64XX_SPI_ENCLK_ENABLE; + writel(val, regs + S3C64XX_SPI_CLK_CFG); + } /* Set Polarity and Phase */ val = readl(regs + S3C64XX_SPI_CH_CFG); @@ -441,17 +446,25 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) writel(val, regs + S3C64XX_SPI_MODE_CFG); - /* Configure Clock */ - val = readl(regs + S3C64XX_SPI_CLK_CFG); - val &= ~S3C64XX_SPI_PSR_MASK; - val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1) - & S3C64XX_SPI_PSR_MASK); - writel(val, regs + S3C64XX_SPI_CLK_CFG); - - /* Enable Clock */ - val = readl(regs + S3C64XX_SPI_CLK_CFG); - val |= S3C64XX_SPI_ENCLK_ENABLE; - writel(val, regs + S3C64XX_SPI_CLK_CFG); + if (sci->clk_from_cmu) { + /* Configure Clock */ + /* There is half-multiplier before the SPI */ + clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); + /* Enable Clock */ + clk_enable(sdd->src_clk); + } else { + /* Configure Clock */ + val = readl(regs + S3C64XX_SPI_CLK_CFG); + val &= ~S3C64XX_SPI_PSR_MASK; + val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1) + & S3C64XX_SPI_PSR_MASK); + writel(val, regs + S3C64XX_SPI_CLK_CFG); + + /* Enable Clock */ + val = readl(regs + S3C64XX_SPI_CLK_CFG); + val |= S3C64XX_SPI_ENCLK_ENABLE; + writel(val, regs + S3C64XX_SPI_CLK_CFG); + } } static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id, @@ -806,7 +819,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi) struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_info *sci; struct spi_message *msg; - u32 psr, speed; unsigned long flags; int err = 0; @@ -849,32 +861,37 @@ static int s3c64xx_spi_setup(struct spi_device *spi) } /* Check if we can provide the requested rate */ - speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); /* Max possible */ - - if (spi->max_speed_hz > speed) - spi->max_speed_hz = speed; - - psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1; - psr &= S3C64XX_SPI_PSR_MASK; - if (psr == S3C64XX_SPI_PSR_MASK) - psr--; + if (!sci->clk_from_cmu) { + u32 psr, speed; + + /* Max possible */ + speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); + + if (spi->max_speed_hz > speed) + spi->max_speed_hz = speed; + + psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1; + psr &= S3C64XX_SPI_PSR_MASK; + if (psr == S3C64XX_SPI_PSR_MASK) + psr--; + + speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); + if (spi->max_speed_hz < speed) { + if (psr+1 < S3C64XX_SPI_PSR_MASK) { + psr++; + } else { + err = -EINVAL; + goto setup_exit; + } + } - speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); - if (spi->max_speed_hz < speed) { - if (psr+1 < S3C64XX_SPI_PSR_MASK) { - psr++; - } else { + speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); + if (spi->max_speed_hz >= speed) + spi->max_speed_hz = speed; + else err = -EINVAL; - goto setup_exit; - } } - speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); - if (spi->max_speed_hz >= speed) - spi->max_speed_hz = speed; - else - err = -EINVAL; - setup_exit: /* setup() returns with device de-selected */ @@ -896,7 +913,8 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) /* Disable Interrupts - we use Polling if not DMA mode */ writel(0, regs + S3C64XX_SPI_INT_EN); - writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT, + if (!sci->clk_from_cmu) + writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT, regs + S3C64XX_SPI_CLK_CFG); writel(0, regs + S3C64XX_SPI_MODE_CFG); writel(0, regs + S3C64XX_SPI_PACKET_CNT); -- cgit v1.2.2 From 0c92ecf10d9fb80b1798d2a9adfdea17f8f5e6d9 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Wed, 29 Sep 2010 17:31:33 +0900 Subject: spi/s3c64xx: Correction for 16,32 bits bus width We can't do without setting channel and bus width to same size. In order to do that, use loop read/writes in polling mode and appropriate burst size in DMA mode. Signed-off-by: Jassi Brar Signed-off-by: Grant Likely --- drivers/spi/spi_s3c64xx.c | 56 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 15 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c index 9e0aa3c7b4d1..795828b90f45 100644 --- a/drivers/spi/spi_s3c64xx.c +++ b/drivers/spi/spi_s3c64xx.c @@ -261,15 +261,25 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, chcfg |= S3C64XX_SPI_CH_TXCH_ON; if (dma_mode) { modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; - s3c2410_dma_config(sdd->tx_dmach, 1); + s3c2410_dma_config(sdd->tx_dmach, sdd->cur_bpw / 8); s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd, xfer->tx_dma, xfer->len); s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START); } else { - unsigned char *buf = (unsigned char *) xfer->tx_buf; - int i = 0; - while (i < xfer->len) - writeb(buf[i++], regs + S3C64XX_SPI_TX_DATA); + switch (sdd->cur_bpw) { + case 32: + iowrite32_rep(regs + S3C64XX_SPI_TX_DATA, + xfer->tx_buf, xfer->len / 4); + break; + case 16: + iowrite16_rep(regs + S3C64XX_SPI_TX_DATA, + xfer->tx_buf, xfer->len / 2); + break; + default: + iowrite8_rep(regs + S3C64XX_SPI_TX_DATA, + xfer->tx_buf, xfer->len); + break; + } } } @@ -286,7 +296,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | S3C64XX_SPI_PACKET_CNT_EN, regs + S3C64XX_SPI_PACKET_CNT); - s3c2410_dma_config(sdd->rx_dmach, 1); + s3c2410_dma_config(sdd->rx_dmach, sdd->cur_bpw / 8); s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd, xfer->rx_dma, xfer->len); s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START); @@ -366,20 +376,26 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, return -EIO; } } else { - unsigned char *buf; - int i; - /* If it was only Tx */ if (xfer->rx_buf == NULL) { sdd->state &= ~TXBUSY; return 0; } - i = 0; - buf = xfer->rx_buf; - while (i < xfer->len) - buf[i++] = readb(regs + S3C64XX_SPI_RX_DATA); - + switch (sdd->cur_bpw) { + case 32: + ioread32_rep(regs + S3C64XX_SPI_RX_DATA, + xfer->rx_buf, xfer->len / 4); + break; + case 16: + ioread16_rep(regs + S3C64XX_SPI_RX_DATA, + xfer->rx_buf, xfer->len / 2); + break; + default: + ioread8_rep(regs + S3C64XX_SPI_RX_DATA, + xfer->rx_buf, xfer->len); + break; + } sdd->state &= ~RXBUSY; } @@ -434,15 +450,17 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) switch (sdd->cur_bpw) { case 32: val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD; + val |= S3C64XX_SPI_MODE_CH_TSZ_WORD; break; case 16: val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD; + val |= S3C64XX_SPI_MODE_CH_TSZ_HALFWORD; break; default: val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE; + val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; break; } - val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; /* Always 8bits wide */ writel(val, regs + S3C64XX_SPI_MODE_CFG); @@ -629,6 +647,14 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd, bpw = xfer->bits_per_word ? : spi->bits_per_word; speed = xfer->speed_hz ? : spi->max_speed_hz; + if (xfer->len % (bpw / 8)) { + dev_err(&spi->dev, + "Xfer length(%u) not a multiple of word size(%u)\n", + xfer->len, bpw / 8); + status = -EIO; + goto out; + } + if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { sdd->cur_bpw = bpw; sdd->cur_speed = speed; -- cgit v1.2.2 From e89524d33deb55de28b4ab171e4b0f89d46b2d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 9 Sep 2010 11:12:12 +0200 Subject: spi/imx: default to m on platforms that have such devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Jason Wang Acked-by: Grant Likely Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- drivers/spi/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 91c2f4f3af10..30aea6de4e37 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -147,6 +147,7 @@ config SPI_IMX tristate "Freescale i.MX SPI controllers" depends on ARCH_MXC select SPI_BITBANG + default m if IMX_HAVE_PLATFORM_SPI_IMX help This enables using the Freescale i.MX SPI controllers in master mode. -- cgit v1.2.2 From f4ba6315cb77a5dcff6664ce1d66ebfe31bcc6b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 9 Sep 2010 15:29:01 +0200 Subject: spi/imx: convert driver to use platform ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This has the advantage not to need to much cpu_is_... macros. Still more when imx51 support is added which has two different spi interfaces which would introduce additional checks on the device id. With this setup it's not possible for the compiler anymore to detect the unused functions, so four additional kconfig symbols are introduced to ifdef out the unneeded functions in the callback array and all these functions are marked with __maybe_unused to suppress the corresponding gcc warnings. Comparing the driver footprint with and without the patch for a mx27 kernel yields: add/remove: 2/0 grow/shrink: 2/0 up/down: 280/0 (280) function old new delta spi_imx_devtype - 192 +192 spi_imx_probe 980 1032 +52 spi_imx_devtype_data - 32 +32 spi_imx_setupxfer 276 280 +4 Later when the platform code is updated to use the platform ids, the autodetection can be removed which will make the driver a bit smaller again. (~60 Bytes in my test.) Acked-by: Jason Wang Acked-by: Grant Likely Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- drivers/spi/Kconfig | 12 ++++ drivers/spi/spi_imx.c | 173 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 143 insertions(+), 42 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 30aea6de4e37..4e9d77bc7d2f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -143,6 +143,18 @@ config SPI_GPIO GPIO operations, you should be able to leverage that for better speed with a custom version of this driver; see the source code. +config SPI_IMX_VER_IMX1 + def_bool y if SOC_IMX1 + +config SPI_IMX_VER_0_0 + def_bool y if SOC_IMX21 || SOC_IMX27 + +config SPI_IMX_VER_0_4 + def_bool y if ARCH_MX31 + +config SPI_IMX_VER_0_7 + def_bool y if ARCH_MX25 || ARCH_MX35 + config SPI_IMX tristate "Freescale i.MX SPI controllers" depends on ARCH_MXC diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 7972e9077473..20cdee3b5a2e 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -59,6 +59,24 @@ struct spi_imx_config { int cs; }; +enum spi_imx_devtype { + SPI_IMX_VER_IMX1, + SPI_IMX_VER_0_0, + SPI_IMX_VER_0_4, + SPI_IMX_VER_0_5, + SPI_IMX_VER_0_7, + SPI_IMX_VER_AUTODETECT, +}; + +struct spi_imx_data; + +struct spi_imx_devtype_data { + void (*intctrl)(struct spi_imx_data *, int); + int (*config)(struct spi_imx_data *, struct spi_imx_config *); + void (*trigger)(struct spi_imx_data *); + int (*rx_available)(struct spi_imx_data *); +}; + struct spi_imx_data { struct spi_bitbang bitbang; @@ -76,11 +94,7 @@ struct spi_imx_data { const void *tx_buf; unsigned int txfifo; /* number of words pushed in tx FIFO */ - /* SoC specific functions */ - void (*intctrl)(struct spi_imx_data *, int); - int (*config)(struct spi_imx_data *, struct spi_imx_config *); - void (*trigger)(struct spi_imx_data *); - int (*rx_available)(struct spi_imx_data *); + struct spi_imx_devtype_data devtype_data; }; #define MXC_SPI_BUF_RX(type) \ @@ -178,7 +192,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin, * the i.MX35 has a slightly different register layout for bits * we do not use here. */ -static void mx31_intctrl(struct spi_imx_data *spi_imx, int enable) +static void __maybe_unused mx31_intctrl(struct spi_imx_data *spi_imx, int enable) { unsigned int val = 0; @@ -190,7 +204,7 @@ static void mx31_intctrl(struct spi_imx_data *spi_imx, int enable) writel(val, spi_imx->base + MXC_CSPIINT); } -static void mx31_trigger(struct spi_imx_data *spi_imx) +static void __maybe_unused mx31_trigger(struct spi_imx_data *spi_imx) { unsigned int reg; @@ -199,7 +213,7 @@ static void mx31_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int mx31_config(struct spi_imx_data *spi_imx, +static int __maybe_unused mx31_config(struct spi_imx_data *spi_imx, struct spi_imx_config *config) { unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; @@ -232,7 +246,7 @@ static int mx31_config(struct spi_imx_data *spi_imx, return 0; } -static int mx31_rx_available(struct spi_imx_data *spi_imx) +static int __maybe_unused mx31_rx_available(struct spi_imx_data *spi_imx) { return readl(spi_imx->base + MX31_CSPISTATUS) & MX31_STATUS_RR; } @@ -250,7 +264,7 @@ static int mx31_rx_available(struct spi_imx_data *spi_imx) #define MX27_CSPICTRL_DR_SHIFT 14 #define MX27_CSPICTRL_CS_SHIFT 19 -static void mx27_intctrl(struct spi_imx_data *spi_imx, int enable) +static void __maybe_unused mx27_intctrl(struct spi_imx_data *spi_imx, int enable) { unsigned int val = 0; @@ -262,7 +276,7 @@ static void mx27_intctrl(struct spi_imx_data *spi_imx, int enable) writel(val, spi_imx->base + MXC_CSPIINT); } -static void mx27_trigger(struct spi_imx_data *spi_imx) +static void __maybe_unused mx27_trigger(struct spi_imx_data *spi_imx) { unsigned int reg; @@ -271,7 +285,7 @@ static void mx27_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int mx27_config(struct spi_imx_data *spi_imx, +static int __maybe_unused mx27_config(struct spi_imx_data *spi_imx, struct spi_imx_config *config) { unsigned int reg = MX27_CSPICTRL_ENABLE | MX27_CSPICTRL_MASTER; @@ -294,7 +308,7 @@ static int mx27_config(struct spi_imx_data *spi_imx, return 0; } -static int mx27_rx_available(struct spi_imx_data *spi_imx) +static int __maybe_unused mx27_rx_available(struct spi_imx_data *spi_imx) { return readl(spi_imx->base + MXC_CSPIINT) & MX27_INTREG_RR; } @@ -310,7 +324,7 @@ static int mx27_rx_available(struct spi_imx_data *spi_imx) #define MX1_CSPICTRL_MASTER (1 << 10) #define MX1_CSPICTRL_DR_SHIFT 13 -static void mx1_intctrl(struct spi_imx_data *spi_imx, int enable) +static void __maybe_unused mx1_intctrl(struct spi_imx_data *spi_imx, int enable) { unsigned int val = 0; @@ -322,7 +336,7 @@ static void mx1_intctrl(struct spi_imx_data *spi_imx, int enable) writel(val, spi_imx->base + MXC_CSPIINT); } -static void mx1_trigger(struct spi_imx_data *spi_imx) +static void __maybe_unused mx1_trigger(struct spi_imx_data *spi_imx) { unsigned int reg; @@ -331,7 +345,7 @@ static void mx1_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int mx1_config(struct spi_imx_data *spi_imx, +static int __maybe_unused mx1_config(struct spi_imx_data *spi_imx, struct spi_imx_config *config) { unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER; @@ -350,11 +364,50 @@ static int mx1_config(struct spi_imx_data *spi_imx, return 0; } -static int mx1_rx_available(struct spi_imx_data *spi_imx) +static int __maybe_unused mx1_rx_available(struct spi_imx_data *spi_imx) { return readl(spi_imx->base + MXC_CSPIINT) & MX1_INTREG_RR; } +/* + * These version numbers are taken from the Freescale driver. Unfortunately it + * doesn't support i.MX1, so this entry doesn't match the scheme. :-( + */ +static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { +#ifdef CONFIG_SPI_IMX_VER_IMX1 + [SPI_IMX_VER_IMX1] = { + .intctrl = mx1_intctrl, + .config = mx1_config, + .trigger = mx1_trigger, + .rx_available = mx1_rx_available, + }, +#endif +#ifdef CONFIG_SPI_IMX_VER_0_0 + [SPI_IMX_VER_0_0] = { + .intctrl = mx27_intctrl, + .config = mx27_config, + .trigger = mx27_trigger, + .rx_available = mx27_rx_available, + }, +#endif +#ifdef CONFIG_SPI_IMX_VER_0_4 + [SPI_IMX_VER_0_4] = { + .intctrl = mx31_intctrl, + .config = mx31_config, + .trigger = mx31_trigger, + .rx_available = mx31_rx_available, + }, +#endif +#ifdef CONFIG_SPI_IMX_VER_0_7 + [SPI_IMX_VER_0_7] = { + .intctrl = mx31_intctrl, + .config = mx31_config, + .trigger = mx31_trigger, + .rx_available = mx31_rx_available, + }, +#endif +}; + static void spi_imx_chipselect(struct spi_device *spi, int is_active) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); @@ -377,14 +430,14 @@ static void spi_imx_push(struct spi_imx_data *spi_imx) spi_imx->txfifo++; } - spi_imx->trigger(spi_imx); + spi_imx->devtype_data.trigger(spi_imx); } static irqreturn_t spi_imx_isr(int irq, void *dev_id) { struct spi_imx_data *spi_imx = dev_id; - while (spi_imx->rx_available(spi_imx)) { + while (spi_imx->devtype_data.rx_available(spi_imx)) { spi_imx->rx(spi_imx); spi_imx->txfifo--; } @@ -398,11 +451,12 @@ static irqreturn_t spi_imx_isr(int irq, void *dev_id) /* No data left to push, but still waiting for rx data, * enable receive data available interrupt. */ - spi_imx->intctrl(spi_imx, MXC_INT_RR); + spi_imx->devtype_data.intctrl( + spi_imx, MXC_INT_RR); return IRQ_HANDLED; } - spi_imx->intctrl(spi_imx, 0); + spi_imx->devtype_data.intctrl(spi_imx, 0); complete(&spi_imx->xfer_done); return IRQ_HANDLED; @@ -439,7 +493,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, } else BUG(); - spi_imx->config(spi_imx, &config); + spi_imx->devtype_data.config(spi_imx, &config); return 0; } @@ -458,7 +512,7 @@ static int spi_imx_transfer(struct spi_device *spi, spi_imx_push(spi_imx); - spi_imx->intctrl(spi_imx, MXC_INT_TE); + spi_imx->devtype_data.intctrl(spi_imx, MXC_INT_TE); wait_for_completion(&spi_imx->xfer_done); @@ -485,6 +539,33 @@ static void spi_imx_cleanup(struct spi_device *spi) { } +static struct platform_device_id spi_imx_devtype[] = { + { + .name = DRIVER_NAME, + .driver_data = SPI_IMX_VER_AUTODETECT, + }, { + .name = "imx1-cspi", + .driver_data = SPI_IMX_VER_IMX1, + }, { + .name = "imx21-cspi", + .driver_data = SPI_IMX_VER_0_0, + }, { + .name = "imx25-cspi", + .driver_data = SPI_IMX_VER_0_7, + }, { + .name = "imx27-cspi", + .driver_data = SPI_IMX_VER_0_0, + }, { + .name = "imx31-cspi", + .driver_data = SPI_IMX_VER_0_4, + }, { + .name = "imx35-cspi", + .driver_data = SPI_IMX_VER_0_7, + }, { + /* sentinel */ + } +}; + static int __devinit spi_imx_probe(struct platform_device *pdev) { struct spi_imx_master *mxc_platform_info; @@ -536,6 +617,31 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) init_completion(&spi_imx->xfer_done); + if (pdev->id_entry->driver_data == SPI_IMX_VER_AUTODETECT) { + if (cpu_is_mx25() || cpu_is_mx35()) + spi_imx->devtype_data = + spi_imx_devtype_data[SPI_IMX_VER_0_7]; + else if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35()) + spi_imx->devtype_data = + spi_imx_devtype_data[SPI_IMX_VER_0_4]; + else if (cpu_is_mx27() || cpu_is_mx21()) + spi_imx->devtype_data = + spi_imx_devtype_data[SPI_IMX_VER_0_0]; + else if (cpu_is_mx1()) + spi_imx->devtype_data = + spi_imx_devtype_data[SPI_IMX_VER_IMX1]; + else + BUG(); + } else + spi_imx->devtype_data = + spi_imx_devtype_data[pdev->id_entry->driver_data]; + + if (!spi_imx->devtype_data.intctrl) { + dev_err(&pdev->dev, "no support for this device compiled in\n"); + ret = -ENODEV; + goto out_gpio_free; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "can't get platform resource\n"); @@ -567,24 +673,6 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) goto out_iounmap; } - if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35()) { - spi_imx->intctrl = mx31_intctrl; - spi_imx->config = mx31_config; - spi_imx->trigger = mx31_trigger; - spi_imx->rx_available = mx31_rx_available; - } else if (cpu_is_mx27() || cpu_is_mx21()) { - spi_imx->intctrl = mx27_intctrl; - spi_imx->config = mx27_config; - spi_imx->trigger = mx27_trigger; - spi_imx->rx_available = mx27_rx_available; - } else if (cpu_is_mx1()) { - spi_imx->intctrl = mx1_intctrl; - spi_imx->config = mx1_config; - spi_imx->trigger = mx1_trigger; - spi_imx->rx_available = mx1_rx_available; - } else - BUG(); - spi_imx->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(spi_imx->clk)) { dev_err(&pdev->dev, "unable to get clock\n"); @@ -603,7 +691,7 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) while (readl(spi_imx->base + MX3_CSPISTAT) & MX3_CSPISTAT_RR) readl(spi_imx->base + MXC_CSPIRXDATA); - spi_imx->intctrl(spi_imx, 0); + spi_imx->devtype_data.intctrl(spi_imx, 0); ret = spi_bitbang_start(&spi_imx->bitbang); if (ret) { @@ -668,6 +756,7 @@ static struct platform_driver spi_imx_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, }, + .id_table = spi_imx_devtype, .probe = spi_imx_probe, .remove = __devexit_p(spi_imx_remove), }; -- cgit v1.2.2 From 1723e66b03c3d131d16f7646752c9782c66ea1ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 10 Sep 2010 09:19:18 +0200 Subject: spi/imx: get rid of more ifs depending on the used cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nearly everything that is needed is provided by the version of the SPI IP. Now the only checks left using cpu_is_... are clk divider tuning on mx21/mx27 and autodetection (which will die soon). Acked-by: Jason Wang Acked-by: Grant Likely Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- drivers/spi/spi_imx.c | 73 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 20 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 20cdee3b5a2e..60a52d173d8f 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -75,6 +75,7 @@ struct spi_imx_devtype_data { int (*config)(struct spi_imx_data *, struct spi_imx_config *); void (*trigger)(struct spi_imx_data *); int (*rx_available)(struct spi_imx_data *); + void (*reset)(struct spi_imx_data *); }; struct spi_imx_data { @@ -213,7 +214,7 @@ static void __maybe_unused mx31_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int __maybe_unused mx31_config(struct spi_imx_data *spi_imx, +static int __maybe_unused spi_imx0_4_config(struct spi_imx_data *spi_imx, struct spi_imx_config *config) { unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; @@ -221,12 +222,7 @@ static int __maybe_unused mx31_config(struct spi_imx_data *spi_imx, reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << MX31_CSPICTRL_DR_SHIFT; - if (cpu_is_mx31()) - reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT; - else if (cpu_is_mx25() || cpu_is_mx35()) { - reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT; - reg |= MX31_CSPICTRL_SSCTL; - } + reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT; if (config->mode & SPI_CPHA) reg |= MX31_CSPICTRL_PHA; @@ -235,10 +231,7 @@ static int __maybe_unused mx31_config(struct spi_imx_data *spi_imx, if (config->mode & SPI_CS_HIGH) reg |= MX31_CSPICTRL_SSPOL; if (config->cs < 0) { - if (cpu_is_mx31()) - reg |= (config->cs + 32) << MX31_CSPICTRL_CS_SHIFT; - else if (cpu_is_mx25() || cpu_is_mx35()) - reg |= (config->cs + 32) << MX35_CSPICTRL_CS_SHIFT; + reg |= (config->cs + 32) << MX31_CSPICTRL_CS_SHIFT; } writel(reg, spi_imx->base + MXC_CSPICTRL); @@ -246,11 +239,43 @@ static int __maybe_unused mx31_config(struct spi_imx_data *spi_imx, return 0; } +static int __maybe_unused spi_imx0_7_config(struct spi_imx_data *spi_imx, + struct spi_imx_config *config) +{ + unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; + + reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << + MX31_CSPICTRL_DR_SHIFT; + + reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT; + reg |= MX31_CSPICTRL_SSCTL; + + if (config->mode & SPI_CPHA) + reg |= MX31_CSPICTRL_PHA; + if (config->mode & SPI_CPOL) + reg |= MX31_CSPICTRL_POL; + if (config->mode & SPI_CS_HIGH) + reg |= MX31_CSPICTRL_SSPOL; + if (config->cs < 0) + reg |= (config->cs + 32) << MX35_CSPICTRL_CS_SHIFT; + + writel(reg, spi_imx->base + MXC_CSPICTRL); + + return 0; +} + static int __maybe_unused mx31_rx_available(struct spi_imx_data *spi_imx) { return readl(spi_imx->base + MX31_CSPISTATUS) & MX31_STATUS_RR; } +static void __maybe_unused spi_imx0_4_reset(struct spi_imx_data *spi_imx) +{ + /* drain receive buffer */ + while (readl(spi_imx->base + MX3_CSPISTAT) & MX3_CSPISTAT_RR) + readl(spi_imx->base + MXC_CSPIRXDATA); +} + #define MX27_INTREG_RR (1 << 4) #define MX27_INTREG_TEEN (1 << 9) #define MX27_INTREG_RREN (1 << 13) @@ -313,6 +338,11 @@ static int __maybe_unused mx27_rx_available(struct spi_imx_data *spi_imx) return readl(spi_imx->base + MXC_CSPIINT) & MX27_INTREG_RR; } +static void __maybe_unused spi_imx0_0_reset(struct spi_imx_data *spi_imx) +{ + writel(1, spi_imx->base + MXC_RESET); +} + #define MX1_INTREG_RR (1 << 3) #define MX1_INTREG_TEEN (1 << 8) #define MX1_INTREG_RREN (1 << 11) @@ -369,6 +399,11 @@ static int __maybe_unused mx1_rx_available(struct spi_imx_data *spi_imx) return readl(spi_imx->base + MXC_CSPIINT) & MX1_INTREG_RR; } +static void __maybe_unused mx1_reset(struct spi_imx_data *spi_imx) +{ + writel(1, spi_imx->base + MXC_RESET); +} + /* * These version numbers are taken from the Freescale driver. Unfortunately it * doesn't support i.MX1, so this entry doesn't match the scheme. :-( @@ -380,6 +415,7 @@ static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { .config = mx1_config, .trigger = mx1_trigger, .rx_available = mx1_rx_available, + .reset = mx1_reset, }, #endif #ifdef CONFIG_SPI_IMX_VER_0_0 @@ -388,22 +424,25 @@ static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { .config = mx27_config, .trigger = mx27_trigger, .rx_available = mx27_rx_available, + .reset = spi_imx0_0_reset, }, #endif #ifdef CONFIG_SPI_IMX_VER_0_4 [SPI_IMX_VER_0_4] = { .intctrl = mx31_intctrl, - .config = mx31_config, + .config = spi_imx0_4_config, .trigger = mx31_trigger, .rx_available = mx31_rx_available, + .reset = spi_imx0_4_reset, }, #endif #ifdef CONFIG_SPI_IMX_VER_0_7 [SPI_IMX_VER_0_7] = { .intctrl = mx31_intctrl, - .config = mx31_config, + .config = spi_imx0_7_config, .trigger = mx31_trigger, .rx_available = mx31_rx_available, + .reset = spi_imx0_4_reset, }, #endif }; @@ -683,13 +722,7 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) clk_enable(spi_imx->clk); spi_imx->spi_clk = clk_get_rate(spi_imx->clk); - if (cpu_is_mx1() || cpu_is_mx21() || cpu_is_mx27()) - writel(1, spi_imx->base + MXC_RESET); - - /* drain receive buffer */ - if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35()) - while (readl(spi_imx->base + MX3_CSPISTAT) & MX3_CSPISTAT_RR) - readl(spi_imx->base + MXC_CSPIRXDATA); + spi_imx->devtype_data.reset(spi_imx); spi_imx->devtype_data.intctrl(spi_imx, 0); -- cgit v1.2.2 From 3b2aa89eb381d2f445aa3c60d8f070a3f55efa63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 10 Sep 2010 09:42:29 +0200 Subject: spi/imx: save the spi chip select in config struct, not the gpio to use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prepares adding support for imx51's eCSPI. This IP has seperate control and config bits for all four supported chip selects, so the config routine needs to know which chip select is being used even if the chipselect is realized by a gpio. Acked-by: Jason Wang Acked-by: Grant Likely Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- drivers/spi/spi_imx.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 60a52d173d8f..23db9840b9ae 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -56,7 +56,7 @@ struct spi_imx_config { unsigned int speed_hz; unsigned int bpw; unsigned int mode; - int cs; + u8 cs; }; enum spi_imx_devtype { @@ -218,6 +218,7 @@ static int __maybe_unused spi_imx0_4_config(struct spi_imx_data *spi_imx, struct spi_imx_config *config) { unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; + int cs = spi_imx->chipselect[config->cs]; reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << MX31_CSPICTRL_DR_SHIFT; @@ -230,9 +231,8 @@ static int __maybe_unused spi_imx0_4_config(struct spi_imx_data *spi_imx, reg |= MX31_CSPICTRL_POL; if (config->mode & SPI_CS_HIGH) reg |= MX31_CSPICTRL_SSPOL; - if (config->cs < 0) { - reg |= (config->cs + 32) << MX31_CSPICTRL_CS_SHIFT; - } + if (cs < 0) + reg |= (cs + 32) << MX31_CSPICTRL_CS_SHIFT; writel(reg, spi_imx->base + MXC_CSPICTRL); @@ -243,6 +243,7 @@ static int __maybe_unused spi_imx0_7_config(struct spi_imx_data *spi_imx, struct spi_imx_config *config) { unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; + int cs = spi_imx->chipselect[config->cs]; reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << MX31_CSPICTRL_DR_SHIFT; @@ -256,8 +257,8 @@ static int __maybe_unused spi_imx0_7_config(struct spi_imx_data *spi_imx, reg |= MX31_CSPICTRL_POL; if (config->mode & SPI_CS_HIGH) reg |= MX31_CSPICTRL_SSPOL; - if (config->cs < 0) - reg |= (config->cs + 32) << MX35_CSPICTRL_CS_SHIFT; + if (cs < 0) + reg |= (cs + 32) << MX35_CSPICTRL_CS_SHIFT; writel(reg, spi_imx->base + MXC_CSPICTRL); @@ -314,6 +315,7 @@ static int __maybe_unused mx27_config(struct spi_imx_data *spi_imx, struct spi_imx_config *config) { unsigned int reg = MX27_CSPICTRL_ENABLE | MX27_CSPICTRL_MASTER; + int cs = spi_imx->chipselect[config->cs]; reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, config->speed_hz) << MX27_CSPICTRL_DR_SHIFT; @@ -325,8 +327,8 @@ static int __maybe_unused mx27_config(struct spi_imx_data *spi_imx, reg |= MX27_CSPICTRL_POL; if (config->mode & SPI_CS_HIGH) reg |= MX27_CSPICTRL_SSPOL; - if (config->cs < 0) - reg |= (config->cs + 32) << MX27_CSPICTRL_CS_SHIFT; + if (cs < 0) + reg |= (cs + 32) << MX27_CSPICTRL_CS_SHIFT; writel(reg, spi_imx->base + MXC_CSPICTRL); @@ -510,7 +512,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, config.bpw = t ? t->bits_per_word : spi->bits_per_word; config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; config.mode = spi->mode; - config.cs = spi_imx->chipselect[spi->chip_select]; + config.cs = spi->chip_select; if (!config.speed_hz) config.speed_hz = spi->max_speed_hz; -- cgit v1.2.2 From 0b599603d8534bc3946a0f07e461c76d7947dfcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 9 Sep 2010 21:02:48 +0200 Subject: spi/imx: add support for imx51's eCSPI and CSPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit i.MX51 comes with two eCSPI interfaces (that are quite different from what was known before---the tried and tested Freescale way) and a CSPI interface that is identical to the devices found on i.MX25 and i.MX35. This patch is a merge of two very similar patches (by Jason Wang and Sascha Hauer resp.) plus a (now hopefully correct) reimplementation of the clock calculation. Acked-by: Jason Wang Acked-by: Grant Likely Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- drivers/spi/Kconfig | 5 +- drivers/spi/spi_imx.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 143 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4e9d77bc7d2f..7e631fa51098 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -153,7 +153,10 @@ config SPI_IMX_VER_0_4 def_bool y if ARCH_MX31 config SPI_IMX_VER_0_7 - def_bool y if ARCH_MX25 || ARCH_MX35 + def_bool y if ARCH_MX25 || ARCH_MX35 || ARCH_MX51 + +config SPI_IMX_VER_2_3 + def_bool y if ARCH_MX51 config SPI_IMX tristate "Freescale i.MX SPI controllers" diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 23db9840b9ae..6bab2cfd93c1 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -65,6 +65,7 @@ enum spi_imx_devtype { SPI_IMX_VER_0_4, SPI_IMX_VER_0_5, SPI_IMX_VER_0_7, + SPI_IMX_VER_2_3, SPI_IMX_VER_AUTODETECT, }; @@ -155,7 +156,7 @@ static unsigned int spi_imx_clkdiv_1(unsigned int fin, return max; } -/* MX1, MX31, MX35 */ +/* MX1, MX31, MX35, MX51 CSPI */ static unsigned int spi_imx_clkdiv_2(unsigned int fin, unsigned int fspi) { @@ -170,6 +171,128 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin, return 7; } +#define SPI_IMX2_3_CTRL 0x08 +#define SPI_IMX2_3_CTRL_ENABLE (1 << 0) +#define SPI_IMX2_3_CTRL_XCH (1 << 2) +#define SPI_IMX2_3_CTRL_MODE(cs) (1 << ((cs) + 4)) +#define SPI_IMX2_3_CTRL_POSTDIV_OFFSET 8 +#define SPI_IMX2_3_CTRL_PREDIV_OFFSET 12 +#define SPI_IMX2_3_CTRL_CS(cs) ((cs) << 18) +#define SPI_IMX2_3_CTRL_BL_OFFSET 20 + +#define SPI_IMX2_3_CONFIG 0x0c +#define SPI_IMX2_3_CONFIG_SCLKPHA(cs) (1 << ((cs) + 0)) +#define SPI_IMX2_3_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4)) +#define SPI_IMX2_3_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8)) +#define SPI_IMX2_3_CONFIG_SSBPOL(cs) (1 << ((cs) + 12)) + +#define SPI_IMX2_3_INT 0x10 +#define SPI_IMX2_3_INT_TEEN (1 << 0) +#define SPI_IMX2_3_INT_RREN (1 << 3) + +#define SPI_IMX2_3_STAT 0x18 +#define SPI_IMX2_3_STAT_RR (1 << 3) + +/* MX51 eCSPI */ +static unsigned int spi_imx2_3_clkdiv(unsigned int fin, unsigned int fspi) +{ + /* + * there are two 4-bit dividers, the pre-divider divides by + * $pre, the post-divider by 2^$post + */ + unsigned int pre, post; + + if (unlikely(fspi > fin)) + return 0; + + post = fls(fin) - fls(fspi); + if (fin > fspi << post) + post++; + + /* now we have: (fin <= fspi << post) with post being minimal */ + + post = max(4U, post) - 4; + if (unlikely(post > 0xf)) { + pr_err("%s: cannot set clock freq: %u (base freq: %u)\n", + __func__, fspi, fin); + return 0xff; + } + + pre = DIV_ROUND_UP(fin, fspi << post) - 1; + + pr_debug("%s: fin: %u, fspi: %u, post: %u, pre: %u\n", + __func__, fin, fspi, post, pre); + return (pre << SPI_IMX2_3_CTRL_PREDIV_OFFSET) | + (post << SPI_IMX2_3_CTRL_POSTDIV_OFFSET); +} + +static void __maybe_unused spi_imx2_3_intctrl(struct spi_imx_data *spi_imx, int enable) +{ + unsigned val = 0; + + if (enable & MXC_INT_TE) + val |= SPI_IMX2_3_INT_TEEN; + + if (enable & MXC_INT_RR) + val |= SPI_IMX2_3_INT_RREN; + + writel(val, spi_imx->base + SPI_IMX2_3_INT); +} + +static void __maybe_unused spi_imx2_3_trigger(struct spi_imx_data *spi_imx) +{ + u32 reg; + + reg = readl(spi_imx->base + SPI_IMX2_3_CTRL); + reg |= SPI_IMX2_3_CTRL_XCH; + writel(reg, spi_imx->base + SPI_IMX2_3_CTRL); +} + +static int __maybe_unused spi_imx2_3_config(struct spi_imx_data *spi_imx, + struct spi_imx_config *config) +{ + u32 ctrl = SPI_IMX2_3_CTRL_ENABLE, cfg = 0; + + /* set master mode */ + ctrl |= SPI_IMX2_3_CTRL_MODE(config->cs); + + /* set clock speed */ + ctrl |= spi_imx2_3_clkdiv(spi_imx->spi_clk, config->speed_hz); + + /* set chip select to use */ + ctrl |= SPI_IMX2_3_CTRL_CS(config->cs); + + ctrl |= (config->bpw - 1) << SPI_IMX2_3_CTRL_BL_OFFSET; + + cfg |= SPI_IMX2_3_CONFIG_SBBCTRL(config->cs); + + if (config->mode & SPI_CPHA) + cfg |= SPI_IMX2_3_CONFIG_SCLKPHA(config->cs); + + if (config->mode & SPI_CPOL) + cfg |= SPI_IMX2_3_CONFIG_SCLKPOL(config->cs); + + if (config->mode & SPI_CS_HIGH) + cfg |= SPI_IMX2_3_CONFIG_SSBPOL(config->cs); + + writel(ctrl, spi_imx->base + SPI_IMX2_3_CTRL); + writel(cfg, spi_imx->base + SPI_IMX2_3_CONFIG); + + return 0; +} + +static int __maybe_unused spi_imx2_3_rx_available(struct spi_imx_data *spi_imx) +{ + return readl(spi_imx->base + SPI_IMX2_3_STAT) & SPI_IMX2_3_STAT_RR; +} + +static void __maybe_unused spi_imx2_3_reset(struct spi_imx_data *spi_imx) +{ + /* drain receive buffer */ + while (spi_imx2_3_rx_available(spi_imx)) + readl(spi_imx->base + MXC_CSPIRXDATA); +} + #define MX31_INTREG_TEEN (1 << 0) #define MX31_INTREG_RREN (1 << 3) @@ -447,6 +570,15 @@ static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { .reset = spi_imx0_4_reset, }, #endif +#ifdef CONFIG_SPI_IMX_VER_2_3 + [SPI_IMX_VER_2_3] = { + .intctrl = spi_imx2_3_intctrl, + .config = spi_imx2_3_config, + .trigger = spi_imx2_3_trigger, + .rx_available = spi_imx2_3_rx_available, + .reset = spi_imx2_3_reset, + }, +#endif }; static void spi_imx_chipselect(struct spi_device *spi, int is_active) @@ -602,6 +734,12 @@ static struct platform_device_id spi_imx_devtype[] = { }, { .name = "imx35-cspi", .driver_data = SPI_IMX_VER_0_7, + }, { + .name = "imx51-cspi", + .driver_data = SPI_IMX_VER_0_7, + }, { + .name = "imx51-ecspi", + .driver_data = SPI_IMX_VER_2_3, }, { /* sentinel */ } -- cgit v1.2.2 From e8b17b5b3f30252b5470dbbf54bc251ddc7fac17 Mon Sep 17 00:00:00 2001 From: Masayuki Ohtake Date: Fri, 8 Oct 2010 12:44:49 -0600 Subject: spi/topcliff: Add topcliff platform controller hub (PCH) spi bus driver Topcliff PCH is the platform controller hub that is going to be used in Intel's upcoming general embedded platform. All IO peripherals in Topcliff PCH are actually devices sitting on AMBA bus. This patch adds a driver for the SPI bus integrated into the Topcliff device. Signed-off-by: Masayuki Ohtake Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/spi_topcliff_pch.c | 1529 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1538 insertions(+) create mode 100644 drivers/spi/spi_topcliff_pch.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 91c2f4f3af10..b32167a686bb 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -298,6 +298,14 @@ config SPI_STMP3XXX help SPI driver for Freescale STMP37xx/378x SoC SSP interface +config SPI_TOPCLIFF_PCH + tristate "PCH SPI Controller" + depends on PCI + help + This driver is for PCH(Platform controller Hub) SPI of Topcliff which + is an IOH(Input/Output Hub) for x86 embedded processor. + This driver can access PCH SPI bus device. + config SPI_TXX9 tristate "Toshiba TXx9 SPI controller" depends on GENERIC_GPIO && CPU_TX49XX diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e9cbd18217a0..65a232d50bc8 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o +obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c new file mode 100644 index 000000000000..58b183f6eec0 --- /dev/null +++ b/drivers/spi/spi_topcliff_pch.c @@ -0,0 +1,1529 @@ +/* + * SPI bus driver for the Topcliff PCH used by Intel SoCs + */ + +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register offsets */ +#define PCH_SPCR 0x00 /* SPI control register */ +#define PCH_SPBRR 0x04 /* SPI baud rate register */ +#define PCH_SPSR 0x08 /* SPI status register */ +#define PCH_SPDWR 0x0C /* SPI write data register */ +#define PCH_SPDRR 0x10 /* SPI read data register */ +#define PCH_SSNXCR 0x18 /* SSN Expand Control Register */ +#define PCH_SRST 0x1C /* SPI reset register */ + +#define PCH_SPSR_TFD 0x000007C0 +#define PCH_SPSR_RFD 0x0000F800 + +#define PCH_READABLE(x) (((x) & PCH_SPSR_RFD)>>11) +#define PCH_WRITABLE(x) (((x) & PCH_SPSR_TFD)>>6) + +#define PCH_RX_THOLD 7 +#define PCH_RX_THOLD_MAX 15 +#define PCH_RX 1 +#define PCH_TX 2 + +/* various interrupts */ +#define PCH_TFI 0x1 +#define PCH_RFI 0x2 +#define PCH_FI 0x4 +#define PCH_ORI 0x8 +#define PCH_MDFI 0x10 + +#define PCH_ALL (PCH_TFI|PCH_RFI|PCH_FI|PCH_ORI|PCH_MDFI) +#define PCH_MAX_BAUDRATE 5000000 +#define PCH_MAX_FIFO_DEPTH 16 + +#define STATUS_RUNNING 1 +#define STATUS_EXITING 2 +#define PCH_SLEEP_TIME 10 + +#define PCH_ADDRESS_SIZE 0x20 + +#define SSN_LOW 0x02U +#define SSN_NO_CONTROL 0x00U +#define PCH_MAX_CS 0xFF +#define PCI_DEVICE_ID_GE_SPI 0x8816 + +#define SPCR_SPE_BIT (1 << 0) +#define SPCR_MSTR_BIT (1 << 1) +#define SPCR_LSBF_BIT (1 << 4) +#define SPCR_CPHA_BIT (1 << 5) +#define SPCR_CPOL_BIT (1 << 6) +#define SPCR_TFIE_BIT (1 << 8) +#define SPCR_RFIE_BIT (1 << 9) +#define SPCR_FIE_BIT (1 << 10) +#define SPCR_ORIE_BIT (1 << 11) +#define SPCR_MDFIE_BIT (1 << 12) +#define SPCR_FICLR_BIT (1 << 24) +#define SPSR_TFI_BIT (1 << 0) +#define SPSR_RFI_BIT (1 << 1) +#define SPSR_FI_BIT (1 << 2) +#define SPBRR_SIZE_BIT (1 << 10) + +#define SPCR_RFIC_FIELD 20 +#define SPCR_TFIC_FIELD 16 + +#define SPSR_INT_BITS 0x1F +#define MASK_SPBRR_SPBR_BITS (~((1 << 10) - 1)) +#define MASK_RFIC_SPCR_BITS (~(0xf << 20)) +#define MASK_TFIC_SPCR_BITS (~(0xf000f << 12)) + +#define PCH_CLOCK_HZ 50000000 +#define PCH_MAX_SPBR 1023 + + +/** + * struct pch_spi_data - Holds the SPI channel specific details + * @io_remap_addr: The remapped PCI base address + * @master: Pointer to the SPI master structure + * @work: Reference to work queue handler + * @wk: Workqueue for carrying out execution of the + * requests + * @wait: Wait queue for waking up upon receiving an + * interrupt. + * @transfer_complete: Status of SPI Transfer + * @bcurrent_msg_processing: Status flag for message processing + * @lock: Lock for protecting this structure + * @queue: SPI Message queue + * @status: Status of the SPI driver + * @bpw_len: Length of data to be transferred in bits per + * word + * @transfer_active: Flag showing active transfer + * @tx_index: Transmit data count; for bookkeeping during + * transfer + * @rx_index: Receive data count; for bookkeeping during + * transfer + * @tx_buff: Buffer for data to be transmitted + * @rx_index: Buffer for Received data + * @n_curnt_chip: The chip number that this SPI driver currently + * operates on + * @current_chip: Reference to the current chip that this SPI + * driver currently operates on + * @current_msg: The current message that this SPI driver is + * handling + * @cur_trans: The current transfer that this SPI driver is + * handling + * @board_dat: Reference to the SPI device data structure + */ +struct pch_spi_data { + void __iomem *io_remap_addr; + struct spi_master *master; + struct work_struct work; + struct workqueue_struct *wk; + wait_queue_head_t wait; + u8 transfer_complete; + u8 bcurrent_msg_processing; + spinlock_t lock; + struct list_head queue; + u8 status; + u32 bpw_len; + u8 transfer_active; + u32 tx_index; + u32 rx_index; + u16 *pkt_tx_buff; + u16 *pkt_rx_buff; + u8 n_curnt_chip; + struct spi_device *current_chip; + struct spi_message *current_msg; + struct spi_transfer *cur_trans; + struct pch_spi_board_data *board_dat; +}; + +/** + * struct pch_spi_board_data - Holds the SPI device specific details + * @pdev: Pointer to the PCI device + * @irq_reg_sts: Status of IRQ registration + * @pci_req_sts: Status of pci_request_regions + * @suspend_sts: Status of suspend + * @data: Pointer to SPI channel data structure + */ +struct pch_spi_board_data { + struct pci_dev *pdev; + u8 irq_reg_sts; + u8 pci_req_sts; + u8 suspend_sts; + struct pch_spi_data *data; +}; + +static struct pci_device_id pch_spi_pcidev_id[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_GE_SPI)}, + {0,} +}; + +static inline void pch_set_bitmsk(u32 *var, u32 bitmask) +{ + *var |= bitmask; +} + +static inline void pch_clr_bitmsk(u32 *var, u32 bitmask) +{ + *var &= (~(bitmask)); +} + +/** + * pch_spi_writereg() - Performs register writes + * @master: Pointer to struct spi_master. + * @idx: Register offset. + * @val: Value to be written to register. + */ +static inline void pch_spi_writereg(struct spi_master *master, int idx, u32 val) +{ + + struct pch_spi_data *data = spi_master_get_devdata(master); + + iowrite32(val, (data->io_remap_addr + idx)); +} + +/** + * pch_spi_readreg() - Performs register reads + * @master: Pointer to struct spi_master. + * @idx: Register offset. + */ +static inline u32 pch_spi_readreg(struct spi_master *master, int idx) +{ + struct pch_spi_data *data = spi_master_get_devdata(master); + + return ioread32(data->io_remap_addr + idx); +} + +/* ope==true:Set bit, ope==false:Clear bit */ +static inline void pch_spi_setclr_bit(u32 *val, u32 pos, bool ope) +{ + if (ope) + *val |= pos; + else + *val &= (~(pos)); +} + +static inline void pch_spi_setclr_reg(struct spi_master *master, int idx, + u32 set, u32 clr) +{ + u32 tmp = pch_spi_readreg(master, idx); + tmp = (tmp & ~clr) | set; + pch_spi_writereg(master, idx, tmp); +} + + +static void pch_spi_set_master_mode(struct spi_master *master) +{ + pch_spi_setclr_reg(master, PCH_SPCR, SPCR_MSTR_BIT, 0); +} + +/** + * pch_spi_clear_fifo() - Clears the Transmit and Receive FIFOs + * @master: Pointer to struct spi_master. + */ +static void pch_spi_clear_fifo(struct spi_master *master) +{ + pch_spi_setclr_reg(master, PCH_SPCR, SPCR_FICLR_BIT, 0); + pch_spi_setclr_reg(master, PCH_SPCR, 0, SPCR_FICLR_BIT); +} + +/** + * ch_spi_disable_interrupts() - Disables specified interrupts + * @master: Pointer to struct spi_master. + * @interrupt: Interrups to be enabled. + */ +static void pch_spi_disable_interrupts(struct spi_master *master, u8 interrupt) +{ + u32 clr_flags = 0; + + if (interrupt & PCH_RFI) + clr_flags |= SPCR_RFIE_BIT; + + if (interrupt & PCH_TFI) + clr_flags |= SPCR_TFIE_BIT; + + if (interrupt & PCH_FI) + clr_flags |= SPCR_FIE_BIT; + + if (interrupt & PCH_ORI) + clr_flags |= SPCR_ORIE_BIT; + + if (interrupt & PCH_MDFI) + clr_flags |= SPCR_MDFIE_BIT; + + pch_spi_setclr_reg(master, PCH_SPCR, 0, clr_flags); + + dev_dbg(&master->dev, "%s clearing bits =%x\n", __func__, clr_flags); +} + +static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, + void __iomem *io_remap_addr) +{ + u32 n_read, tx_index, rx_index, bpw_len; + u16 *pkt_rx_buffer, *pkt_tx_buff; + int read_cnt; + u32 reg_spcr_val; + void __iomem *spsr; + void __iomem *spdrr; + void __iomem *spdwr; + + spsr = io_remap_addr + PCH_SPSR; + iowrite32(reg_spsr_val, spsr); + + if (data->transfer_active) { + rx_index = data->rx_index; + tx_index = data->tx_index; + bpw_len = data->bpw_len; + pkt_rx_buffer = data->pkt_rx_buff; + pkt_tx_buff = data->pkt_tx_buff; + + spdrr = io_remap_addr + PCH_SPDRR; + spdwr = io_remap_addr + PCH_SPDWR; + + n_read = PCH_READABLE(reg_spsr_val); + + for (read_cnt = 0; (read_cnt < n_read); read_cnt++) { + pkt_rx_buffer[rx_index++] = ioread32(spdrr); + if (tx_index < bpw_len) + iowrite32(pkt_tx_buff[tx_index++], spdwr); + } + + /* disable RFI if not needed */ + if ((bpw_len - rx_index) <= PCH_MAX_FIFO_DEPTH) { + reg_spcr_val = ioread32(io_remap_addr + PCH_SPCR); + + /* disable RFI */ + pch_clr_bitmsk(®_spcr_val, SPCR_RFIE_BIT); + + /* reset rx threshold */ + reg_spcr_val &= MASK_RFIC_SPCR_BITS; + reg_spcr_val |= (PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD); + iowrite32(((reg_spcr_val) &= (~(SPCR_RFIE_BIT))), + (io_remap_addr + PCH_SPCR)); + } + + /* update counts */ + data->tx_index = tx_index; + data->rx_index = rx_index; + + } + + /* if transfer complete interrupt */ + if (reg_spsr_val & SPSR_FI_BIT) { + /* disable FI & RFI interrupts */ + pch_spi_disable_interrupts(data->master, PCH_FI | PCH_RFI); + + /* transfer is completed;inform pch_spi_process_messages */ + data->transfer_complete = true; + wake_up(&data->wait); + } +} + + +/** + * pch_spi_handler() - Interrupt handler + * @irq: The interrupt number. + * @dev_id: Pointer to struct pch_spi_board_data. + */ +static irqreturn_t pch_spi_handler(int irq, void *dev_id) +{ + u32 reg_spsr_val; + struct pch_spi_data *data; + void __iomem *spsr; + void __iomem *io_remap_addr; + irqreturn_t ret = IRQ_NONE; + + struct pch_spi_board_data *board_dat = dev_id; + + if (board_dat->suspend_sts) { + dev_dbg(&board_dat->pdev->dev, + "%s returning due to suspend\n", __func__); + return IRQ_NONE; + } + + data = board_dat->data; + io_remap_addr = data->io_remap_addr; + spsr = io_remap_addr + PCH_SPSR; + + reg_spsr_val = ioread32(spsr); + + /* Check if the interrupt is for SPI device */ + + if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) { + pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr); + ret = IRQ_HANDLED; + } + + dev_dbg(&board_dat->pdev->dev, "%s EXIT return value=%d\n", + __func__, ret); + + return ret; +} + +/** + * pch_spi_set_baud_rate() - Sets SPBR field in SPBRR + * @master: Pointer to struct spi_master. + * @speed_hz: Baud rate. + */ +static void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz) +{ + u32 n_spbr; + + n_spbr = PCH_CLOCK_HZ / (speed_hz * 2); + + /* if baud rate is less than we can support limit it */ + + if (n_spbr > PCH_MAX_SPBR) + n_spbr = PCH_MAX_SPBR; + + pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, ~MASK_SPBRR_SPBR_BITS); +} + +/** + * pch_spi_set_bits_per_word() - Sets SIZE field in SPBRR + * @master: Pointer to struct spi_master. + * @bits_per_word: Bits per word for SPI transfer. + */ +static void pch_spi_set_bits_per_word(struct spi_master *master, + u8 bits_per_word) +{ + if (bits_per_word == 8) + pch_spi_setclr_reg(master, PCH_SPBRR, 0, SPBRR_SIZE_BIT); + else + pch_spi_setclr_reg(master, PCH_SPBRR, SPBRR_SIZE_BIT, 0); +} + +/** + * pch_spi_setup_transfer() - Configures the PCH SPI hardware for transfer + * @spi: Pointer to struct spi_device. + */ +static void pch_spi_setup_transfer(struct spi_device *spi) +{ + u32 reg_spcr_val; + + dev_dbg(&spi->dev, "%s SPBRR content =%x setting baud rate=%d\n", + __func__, pch_spi_readreg(spi->master, PCH_SPBRR), + spi->max_speed_hz); + + pch_spi_set_baud_rate(spi->master, spi->max_speed_hz); + + /* set bits per word */ + pch_spi_set_bits_per_word(spi->master, spi->bits_per_word); + + if (spi->mode & SPI_LSB_FIRST) + pch_spi_setclr_reg(spi->master, PCH_SPCR, 0, SPCR_LSBF_BIT); + else + pch_spi_setclr_reg(spi->master, PCH_SPCR, SPCR_LSBF_BIT, 0); + + if (spi->mode & SPI_CPOL) + pch_spi_setclr_reg(spi->master, PCH_SPCR, SPCR_CPOL_BIT, 0); + else + pch_spi_setclr_reg(spi->master, PCH_SPCR, 0, SPCR_CPOL_BIT); + + if (spi->mode & SPI_CPHA) + pch_spi_setclr_reg(spi->master, PCH_SPCR, SPCR_CPHA_BIT, 0); + else + pch_spi_setclr_reg(spi->master, PCH_SPCR, 0, SPCR_CPHA_BIT); + + dev_dbg(&spi->dev, + "%s SPCR content after setting LSB/MSB and MODE= %x\n", + __func__, reg_spcr_val); + + /* Clear the FIFO by toggling FICLR to 1 and back to 0 */ + pch_spi_clear_fifo(spi->master); +} + +/** + * pch_spi_enable_interrupts() - Enables specified interrupts + * @master: Pointer to struct spi_master. + * @interrupt: Interrups to be enabled. + */ +static void pch_spi_enable_interrupts(struct spi_master *master, u8 interrupt) +{ + u32 reg_val_spcr; + + dev_dbg(&master->dev, "%s SPCR content=%x\n", __func__, reg_val_spcr); + + if (interrupt & PCH_RFI) { + /* set RFIE bit in SPCR */ + dev_dbg(&master->dev, "setting RFI in %s\n", __func__); + pch_spi_setclr_reg(master, PCH_SPCR, SPCR_RFIE_BIT, 0); + } + + if (interrupt & PCH_TFI) { + /* set TFIE bit in SPCR */ + dev_dbg(&master->dev, "setting TFI in %s\n", __func__); + pch_spi_setclr_reg(master, PCH_SPCR, SPCR_TFIE_BIT, 0); + } + + if (interrupt & PCH_FI) { + /* set FIE bit in SPCR */ + dev_dbg(&master->dev, "setting FI in %s\n", __func__); + pch_spi_setclr_reg(master, PCH_SPCR, SPCR_FIE_BIT, 0); + } + + if (interrupt & PCH_ORI) { + /* set ORIE bit in SPCR */ + dev_dbg(&master->dev, "setting ORI in %s\n", __func__); + pch_spi_setclr_reg(master, PCH_SPCR, SPCR_ORIE_BIT, 0); + } + + if (interrupt & PCH_MDFI) { + /* set MODFIE bit in SPCR */ + dev_dbg(&master->dev, "setting MDFI in %s\n", __func__); + pch_spi_setclr_reg(master, PCH_SPCR, SPCR_MDFIE_BIT, 0); + } +} + +/** + * pch_spi_set_threshold() - Sets Tx/Rx FIFO thresholds + * @spi: Pointer to struct spi_device. + * @threshold: Threshold value to be set. + * @dir: Rx or Tx threshold to be set. + */ +static void pch_spi_set_threshold(struct spi_device *spi, u32 threshold, u8 dir) +{ + + if (dir == PCH_RX) { + dev_dbg(&spi->dev, "%s setting Rx threshold\n", __func__); + pch_spi_setclr_reg(spi->master, PCH_SPCR, + threshold << SPCR_RFIC_FIELD, + ~MASK_RFIC_SPCR_BITS); + + } else if (dir == PCH_TX) { + dev_dbg(&spi->dev, "%s setting Tx threshold\n", __func__); + pch_spi_setclr_reg(spi->master, PCH_SPCR, + (threshold << SPCR_TFIC_FIELD) , + ~MASK_TFIC_SPCR_BITS); + } +} + +/** + * pch_spi_reset() - Clears SPI registers + * @master: Pointer to struct spi_master. + */ +static void pch_spi_reset(struct spi_master *master) +{ + /* write 1 to reset SPI */ + pch_spi_writereg(master, PCH_SRST, 0x1); + + /* clear reset */ + pch_spi_writereg(master, PCH_SRST, 0x0); +} + +static int pch_spi_setup(struct spi_device *pspi) +{ + /* check bits per word */ + if ((pspi->bits_per_word) == 0) { + pspi->bits_per_word = 8; + dev_dbg(&pspi->dev, "%s 8 bits per word\n", __func__); + } + + if (((pspi->bits_per_word) != 8) && + ((pspi->bits_per_word != 16))) { + dev_err(&pspi->dev, "%s Invalid bits per word\n", __func__); + return -EINVAL; + } + + /* Check baud rate setting */ + /* if baud rate of chip is greater than + max we can support,return error */ + if ((pspi->max_speed_hz) > PCH_MAX_BAUDRATE) + pspi->max_speed_hz = PCH_MAX_BAUDRATE; + + dev_dbg(&pspi->dev, "%s MODE = %x\n", __func__, + ((pspi->mode) & (SPI_CPOL | SPI_CPHA))); + + return 0; +} + +static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) +{ + + struct spi_transfer *transfer; + struct pch_spi_data *data = spi_master_get_devdata(pspi->master); + int retval; + unsigned long flags; + + /* validate spi message and baud rate */ + if (unlikely((list_empty(&pmsg->transfers) == 1) || + (pspi->max_speed_hz == 0))) { + if (list_empty(&pmsg->transfers) == 1) + dev_err(&pspi->dev, "%s list empty\n", __func__); + + if ((pspi->max_speed_hz) == 0) { + dev_err(&pspi->dev, "%s pch_spi_tranfer maxspeed=%d\n", + __func__, pspi->max_speed_hz); + } + dev_err(&pspi->dev, "%s returning EINVAL\n", __func__); + + retval = -EINVAL; + goto err_out; + } + + dev_dbg(&pspi->dev, "%s Transfer List not empty. " + "Transfer Speed is set.\n", __func__); + + spin_lock_irqsave(&data->lock, flags); + + /* validate Tx/Rx buffers and Transfer length */ + list_for_each_entry(transfer, &pmsg->transfers, transfer_list) { + if ((!(transfer->tx_buf)) && (!(transfer->rx_buf))) { + dev_err(&pspi->dev, + "%s Tx and Rx buffer NULL\n", __func__); + retval = -EINVAL; + goto err_return_spinlock; + } + + if (!(transfer->len)) { + dev_err(&pspi->dev, "%s Transfer length invalid\n", + __func__); + retval = -EINVAL; + goto err_return_spinlock; + } + + dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length" + " valid\n", __func__); + + /* if baud rate hs been specified validate the same */ + if (transfer->speed_hz) { + if ((transfer->speed_hz) > PCH_MAX_BAUDRATE) + transfer->speed_hz = PCH_MAX_BAUDRATE; + } + + /* if bits per word has been specified validate the same */ + if (transfer->bits_per_word) { + if ((transfer->bits_per_word != 8) + && (transfer->bits_per_word != 16)) { + retval = -EINVAL; + dev_err(&pspi->dev, + "%s Invalid bits per word\n", __func__); + goto err_return_spinlock; + } + } + } + + /* We won't process any messages if we have been asked to terminate */ + + if (STATUS_EXITING == (data->status)) { + dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__); + retval = -ESHUTDOWN; + goto err_return_spinlock; + } + + /* If suspended ,return -EINVAL */ + if (data->board_dat->suspend_sts) { + dev_err(&pspi->dev, + "%s bSuspending= true returning EINVAL\n", __func__); + retval = -EINVAL; + goto err_return_spinlock; + } + + /* set status of message */ + pmsg->actual_length = 0; + + dev_dbg(&pspi->dev, "%s - pmsg->status =%d\n", __func__, pmsg->status); + + pmsg->status = -EINPROGRESS; + + /* add message to queue */ + list_add_tail(&pmsg->queue, &data->queue); + + dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__); + + /* schedule work queue to run */ + queue_work(data->wk, &data->work); + + dev_dbg(&pspi->dev, "%s - Invoked queue work\n", __func__); + + retval = 0; + +err_return_spinlock: + spin_unlock_irqrestore(&data->lock, flags); +err_out: + dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval); + return retval; +} + +static inline void pch_spi_select_chip(struct pch_spi_data *data, + struct spi_device *pspi) +{ + if ((data->current_chip) != NULL) { + if ((pspi->chip_select) != (data->n_curnt_chip)) { + dev_dbg(&pspi->dev, + "%s : different slave-Invoking\n", __func__); + data->current_chip = NULL; + } + } + + data->current_chip = pspi; + + data->n_curnt_chip = data->current_chip->chip_select; + + dev_dbg(&pspi->dev, "%s :Invoking pch_spi_setup_transfer\n", __func__); + pch_spi_setup_transfer(pspi); +} + +static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw, + struct spi_message **ppmsg) +{ + int b_mem_fail; + int size; + u32 n_writes; + int j; + struct spi_message *pmsg; + const u8 *tx_buf; + const u16 *tx_sbuf; + + pmsg = *ppmsg; + + /* set baud rate if needed */ + if (data->cur_trans->speed_hz) { + dev_dbg(&data->master->dev, + "%s:pctrldatasetting baud rate\n", __func__); + pch_spi_set_baud_rate(data->master, + (data->cur_trans->speed_hz)); + } + + /* set bits per word if needed */ + if ((data->cur_trans->bits_per_word) && + ((data->current_msg->spi->bits_per_word) != + (data->cur_trans->bits_per_word))) { + dev_dbg(&data->master->dev, + "%s:setting bits per word\n", __func__); + pch_spi_set_bits_per_word(data->master, + (data->cur_trans->bits_per_word)); + *bpw = data->cur_trans->bits_per_word; + } else { + *bpw = data->current_msg->spi->bits_per_word; + } + + /* reset Tx/Rx index */ + data->tx_index = 0; + data->rx_index = 0; + + data->bpw_len = data->cur_trans->len / (*bpw / 8); + b_mem_fail = false; + + /* find alloc size */ + size = (data->cur_trans->len) * (sizeof(*(data->pkt_tx_buff))); + /* allocate memory for pkt_tx_buff & pkt_rx_buffer */ + data->pkt_tx_buff = kzalloc(size, GFP_KERNEL); + + if (data->pkt_tx_buff != NULL) { + data->pkt_rx_buff = kzalloc(size, GFP_KERNEL); + + if (data->pkt_rx_buff == NULL) { + b_mem_fail = true; + kfree(data->pkt_tx_buff); + } + } else { + b_mem_fail = true; + } + + if (b_mem_fail) { + /* flush queue and set status of all transfers to -ENOMEM */ + dev_err(&data->master->dev, + "Kzalloc fail in %s messages\n", __func__); + list_for_each_entry(pmsg, data->queue.next, queue) { + pmsg->status = -ENOMEM; + + if (pmsg->complete != 0) + pmsg->complete(pmsg->context); + + /* delete from queue */ + list_del_init(&pmsg->queue); + } + + return; + } + + /* copy Tx Data */ + if ((data->cur_trans->tx_buf) != NULL) { + if (*bpw == 8) { + for (j = 0; j < (data->bpw_len); j++) { + tx_buf = data->cur_trans->tx_buf; + data->pkt_tx_buff[j] = tx_buf[j]; + } + } else { + for (j = 0; j < (data->bpw_len); j++) { + tx_sbuf = data->cur_trans->tx_buf; + data->pkt_tx_buff[j] = tx_sbuf[j]; + } + } + } + + /* if len greater than PCH_MAX_FIFO_DEPTH, write 16,else len bytes */ + if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) + n_writes = PCH_MAX_FIFO_DEPTH; + else + n_writes = (data->bpw_len); + + dev_dbg(&data->master->dev, "\n%s:Pulling down SSN low - writing " + "0x2 to SSNXCR\n", __func__); + pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW); + + for (j = 0; j < n_writes; j++) { + pch_spi_writereg(data->master, PCH_SPDWR, + data->pkt_tx_buff[j]); + } + + /* update tx_index */ + data->tx_index = j; + + /* reset transfer complete flag */ + data->transfer_complete = false; + data->transfer_active = true; +} + + +static void pch_spi_nomore_transfer(struct pch_spi_data *data, + struct spi_message *pmsg) +{ + dev_dbg(&data->master->dev, + "%s:no more transfers in this message\n", __func__); + /* Invoke complete callback + [To the spi core..indicating end of transfer] */ + data->current_msg->status = 0; + + if ((data->current_msg->complete) != 0) { + dev_dbg(&data->master->dev, + "%s:Invoking callback of SPI core\n", __func__); + data->current_msg->complete(data->current_msg->context); + } + + /* update status in global variable */ + data->bcurrent_msg_processing = false; + + dev_dbg(&data->master->dev, + "%s:data->bcurrent_msg_processing = false\n", __func__); + + data->current_msg = NULL; + data->cur_trans = NULL; + + /* check if we have items in list and not suspending */ + /* return 1 if list empty */ + if ((list_empty(&data->queue) == 0) && + (!(data->board_dat->suspend_sts)) + && (data->status != STATUS_EXITING)) { + /* We have some more work to do (either there is more tranint + bpw;sfer requests in the current message or there are + more messages) + */ + dev_dbg(&data->master->dev, + "%s:we have pending messages-Invoking queue_work\n", + __func__); + queue_work(data->wk, &data->work); + } else if ((data->board_dat->suspend_sts) || + (data->status == STATUS_EXITING)) { + dev_dbg(&data->master->dev, + "%s suspend/remove initiated, flushing queue\n", + __func__); + list_for_each_entry(pmsg, data->queue.next, queue) { + pmsg->status = -EIO; + + if (pmsg->complete != 0) + pmsg->complete(pmsg->context); + + /* delete from queue */ + list_del_init(&pmsg->queue); + } + } +} + +static void pch_spi_set_ir(struct pch_spi_data *data) +{ + u32 reg_spcr_val; + + /* enable interrupts */ + if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) { + /* set receive threhold to PCH_RX_THOLD */ + pch_spi_set_threshold(data->current_chip, PCH_RX_THOLD, PCH_RX); + /* enable FI and RFI interrupts */ + pch_spi_enable_interrupts(data->master, PCH_RFI | PCH_FI); + } else { + /* set receive threhold to maximum */ + pch_spi_set_threshold(data->current_chip, PCH_RX_THOLD_MAX, + PCH_RX); + /* enable FI interrupt */ + pch_spi_enable_interrupts(data->master, PCH_FI); + } + + dev_dbg(&data->master->dev, + "%s:invoking pch_spi_set_enable to enable SPI\n", __func__); + + /* SPI set enable */ + reg_spcr_val = pch_spi_readreg(data->current_chip->master, PCH_SPCR); + pch_set_bitmsk(®_spcr_val, SPCR_SPE_BIT); + pch_spi_writereg(data->current_chip->master, PCH_SPCR, reg_spcr_val); + + + /* Wait until the transfer completes; go to sleep after + initiating the transfer. */ + dev_dbg(&data->master->dev, + "%s:waiting for transfer to get over\n", __func__); + + wait_event_interruptible(data->wait, data->transfer_complete); + + pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL); + dev_dbg(&data->master->dev, + "%s:no more control over SSN-writing 0 to SSNXCR.", __func__); + + data->transfer_active = false; + dev_dbg(&data->master->dev, + "%s set data->transfer_active = false\n", __func__); + + /* clear all interrupts */ + pch_spi_writereg(data->master, PCH_SPSR, + (pch_spi_readreg(data->master, PCH_SPSR))); + /* disable interrupts */ + pch_spi_disable_interrupts(data->master, PCH_ALL); +} + +static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw) +{ + int j; + u8 *rx_buf; + u16 *rx_sbuf; + + /* copy Rx Data */ + if (!(data->cur_trans->rx_buf)) + return; + + if (bpw == 8) { + for (j = 0; j < (data->bpw_len); j++) { + rx_buf = data->cur_trans->rx_buf; + rx_buf[j] = (data->pkt_rx_buff[j]) & 0xFF; + } + } else { + for (j = 0; j < (data->bpw_len); j++) { + rx_sbuf = data->cur_trans->rx_buf; + rx_sbuf[j] = data->pkt_rx_buff[j]; + } + } +} + + +static void pch_spi_process_messages(struct work_struct *pwork) +{ + struct spi_message *pmsg; + int bpw; + + struct pch_spi_data *data = + container_of(pwork, struct pch_spi_data, work); + dev_dbg(&data->master->dev, "%s data initialized\n", __func__); + + spin_lock(&data->lock); + + /* check if suspend has been initiated;if yes flush queue */ + if ((data->board_dat->suspend_sts) || + (data->status == STATUS_EXITING)) { + dev_dbg(&data->master->dev, + "%s suspend/remove initiated,flushing queue\n", + __func__); + list_for_each_entry(pmsg, data->queue.next, queue) { + pmsg->status = -EIO; + + if (pmsg->complete != 0) { + spin_unlock(&data->lock); + pmsg->complete(pmsg->context); + spin_lock(&data->lock); + } + + /* delete from queue */ + list_del_init(&pmsg->queue); + } + + spin_unlock(&data->lock); + return; + } + + data->bcurrent_msg_processing = true; + dev_dbg(&data->master->dev, + "%s Set data->bcurrent_msg_processing= true\n", __func__); + + /* Get the message from the queue and delete it from there. */ + data->current_msg = + list_entry(data->queue.next, struct spi_message, queue); + + list_del_init(&data->current_msg->queue); + + data->current_msg->status = 0; + + pch_spi_select_chip(data, data->current_msg->spi); + + spin_unlock(&data->lock); + + do { + /* If we are already processing a message get the next + transfer structure from the message otherwise retrieve + the 1st transfer request from the message. */ + spin_lock(&data->lock); + + if (data->cur_trans == NULL) { + data->cur_trans = + list_entry(data->current_msg->transfers. + next, struct spi_transfer, + transfer_list); + dev_dbg(&data->master->dev, + "%s :Getting 1st transfer message\n", __func__); + } else { + data->cur_trans = + list_entry(data->cur_trans->transfer_list.next, + struct spi_transfer, + transfer_list); + dev_dbg(&data->master->dev, + "%s :Getting next transfer message\n", + __func__); + } + + spin_unlock(&data->lock); + + pch_spi_set_tx(data, &bpw, &pmsg); + + /* Control interrupt*/ + pch_spi_set_ir(data); + + /* Disable SPI transfer */ + pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, 0, + SPCR_SPE_BIT); + + /* clear FIFO */ + pch_spi_clear_fifo(data->master); + + /* copy Rx Data */ + pch_spi_copy_rx_data(data, bpw); + + /* free memory */ + kfree(data->pkt_rx_buff); + data->pkt_rx_buff = NULL; + + kfree(data->pkt_tx_buff); + data->pkt_tx_buff = NULL; + + /* increment message count */ + data->current_msg->actual_length += data->cur_trans->len; + + dev_dbg(&data->master->dev, + "%s:data->current_msg->actual_length=%d\n", + __func__, data->current_msg->actual_length); + + /* check for delay */ + if (data->cur_trans->delay_usecs) { + dev_dbg(&data->master->dev, "%s:" + "delay in usec=%d\n", __func__, + data->cur_trans->delay_usecs); + udelay(data->cur_trans->delay_usecs); + } + + spin_lock(&data->lock); + + /* No more transfer in this message. */ + if ((data->cur_trans->transfer_list.next) == + &(data->current_msg->transfers)) { + pch_spi_nomore_transfer(data, pmsg); + } + + spin_unlock(&data->lock); + + } while ((data->cur_trans) != NULL); +} + +static void pch_spi_free_resources(struct pch_spi_board_data *board_dat) +{ + dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__); + + /* free workqueue */ + if (board_dat->data->wk != NULL) { + destroy_workqueue(board_dat->data->wk); + board_dat->data->wk = NULL; + dev_dbg(&board_dat->pdev->dev, + "%s destroy_workqueue invoked successfully\n", + __func__); + } + + /* disable interrupts & free IRQ */ + if (board_dat->irq_reg_sts) { + /* disable interrupts */ + pch_spi_disable_interrupts(board_dat->data-> + master, PCH_ALL); + dev_dbg(&board_dat->pdev->dev, + "%s pch_spi_disable_interrupts invoked " + "successfully\n", __func__); + + /* free IRQ */ + free_irq(board_dat->pdev->irq, (void *)board_dat); + + dev_dbg(&board_dat->pdev->dev, + "%s free_irq invoked successfully\n", __func__); + + board_dat->irq_reg_sts = false; + } + + /* unmap PCI base address */ + if ((board_dat->data->io_remap_addr) != 0) { + pci_iounmap(board_dat->pdev, board_dat->data->io_remap_addr); + + board_dat->data->io_remap_addr = 0; + + dev_dbg(&board_dat->pdev->dev, + "%s pci_iounmap invoked successfully\n", __func__); + } + + /* release PCI region */ + if (board_dat->pci_req_sts) { + pci_release_regions(board_dat->pdev); + dev_dbg(&board_dat->pdev->dev, + "%s pci_release_regions invoked successfully\n", + __func__); + board_dat->pci_req_sts = false; + } +} + +static int pch_spi_get_resources(struct pch_spi_board_data *board_dat) +{ + void __iomem *io_remap_addr; + int retval; + dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__); + + /* iniatize queue of pending messages */ + INIT_LIST_HEAD(&(board_dat->data->queue)); + + /* initialize spin locks */ + spin_lock_init(&(board_dat->data->lock)); + + /* set channel status */ + board_dat->data->status = STATUS_RUNNING; + + /* initialize work structure */ + INIT_WORK(&(board_dat->data->work), + pch_spi_process_messages); + + /* initialize wait queues */ + init_waitqueue_head(&(board_dat->data->wait)); + + /* create workqueue */ + board_dat->data->wk = create_singlethread_workqueue(KBUILD_MODNAME); + + if ((board_dat->data->wk) == NULL) { + dev_err(&board_dat->pdev->dev, + "%s create_singlet hread_workqueue failed\n", __func__); + retval = -EBUSY; + goto err_return; + } + + dev_dbg(&board_dat->pdev->dev, + "%s create_singlethread_workqueue success\n", __func__); + + retval = pci_request_regions(board_dat->pdev, KBUILD_MODNAME); + if (retval != 0) { + dev_err(&board_dat->pdev->dev, + "%s request_region failed\n", __func__); + goto err_return; + } + + board_dat->pci_req_sts = true; + + io_remap_addr = pci_iomap(board_dat->pdev, 1, 0); + + if (io_remap_addr == 0) { + dev_err(&board_dat->pdev->dev, + "%s pci_iomap failed\n", __func__); + retval = -ENOMEM; + goto err_return; + } + + /* calculate base address for all channels */ + board_dat->data->io_remap_addr = io_remap_addr; + + /* reset PCH SPI h/w */ + pch_spi_reset(board_dat->data->master); + dev_dbg(&board_dat->pdev->dev, + "%s pch_spi_reset invoked successfully\n", __func__); + + /* register IRQ */ + retval = request_irq(board_dat->pdev->irq, pch_spi_handler, + IRQF_SHARED, KBUILD_MODNAME, (void *)board_dat); + if (retval != 0) { + dev_err(&board_dat->pdev->dev, + "%s request_irq failed\n", __func__); + goto err_return; + } + + dev_dbg(&board_dat->pdev->dev, "%s request_irq returned=%d\n", + __func__, retval); + + board_dat->irq_reg_sts = true; + dev_dbg(&board_dat->pdev->dev, + "%s data->irq_reg_sts=true\n", __func__); + +err_return: + if (retval != 0) { + dev_err(&board_dat->pdev->dev, + "%s FAIL:invoking pch_spi_free_resources\n", __func__); + pch_spi_free_resources(board_dat); + } + + dev_dbg(&board_dat->pdev->dev, "%s Return=%d\n", __func__, retval); + + return retval; +} + +static int pch_spi_check_request_pending(struct pch_spi_board_data *board_dat) +{ + int sts; + u16 count; + + count = 500; + spin_lock(&(board_dat->data->lock)); + board_dat->data->status = STATUS_EXITING; + + while ((list_empty(&(board_dat->data->queue)) == 0) && + (--count)) { + dev_dbg(&board_dat->pdev->dev, + "%s :queue not empty\n", __func__); + spin_unlock(&(board_dat->data->lock)); + msleep(PCH_SLEEP_TIME); + spin_lock(&(board_dat->data->lock)); + } + + spin_unlock(&(board_dat->data->lock)); + + if (count) { + sts = 0; + dev_dbg(&board_dat->pdev->dev, "%s :queue empty\n", __func__); + } else { + sts = -EBUSY; + } + + dev_dbg(&board_dat->pdev->dev, "%s : EXIT=%d\n", __func__, sts); + + return sts; +} + +static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + + struct spi_master *master; + + struct pch_spi_board_data *board_dat; + int retval; + + dev_dbg(&pdev->dev, "%s ENTRY\n", __func__); + + /* allocate memory for private data */ + board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL); + if (board_dat == NULL) { + dev_err(&pdev->dev, + " %s memory allocation for private data failed\n", + __func__); + retval = -ENOMEM; + goto err_kmalloc; + } + + dev_dbg(&pdev->dev, + "%s memory allocation for private data success\n", __func__); + + /* enable PCI device */ + retval = pci_enable_device(pdev); + if (retval != 0) { + dev_err(&pdev->dev, "%s pci_enable_device FAILED\n", __func__); + + goto err_pci_en_device; + } + + dev_dbg(&pdev->dev, "%s pci_enable_device returned=%d\n", + __func__, retval); + + board_dat->pdev = pdev; + + /* alllocate memory for SPI master */ + master = spi_alloc_master(&pdev->dev, sizeof(struct pch_spi_data)); + if (master == NULL) { + retval = -ENOMEM; + dev_err(&pdev->dev, "%s Fail.\n", __func__); + goto err_spi_alloc_master; + } + + dev_dbg(&pdev->dev, + "%s spi_alloc_master returned non NULL\n", __func__); + + /* initialize members of SPI master */ + master->bus_num = -1; + master->num_chipselect = PCH_MAX_CS; + master->setup = pch_spi_setup; + master->transfer = pch_spi_transfer; + dev_dbg(&pdev->dev, + "%s transfer member of SPI master initialized\n", __func__); + + board_dat->data = spi_master_get_devdata(master); + + board_dat->data->master = master; + board_dat->data->n_curnt_chip = 255; + board_dat->data->board_dat = board_dat; + + /* allocate resources for PCH SPI */ + retval = pch_spi_get_resources(board_dat); + if (retval != 0) { + dev_err(&pdev->dev, "%s fail(retval=%d)\n", + __func__, retval); + goto err_spi_get_resources; + } + + dev_dbg(&pdev->dev, "%s pch_spi_get_resources returned=%d\n", + __func__, retval); + + /* save private data in dev */ + pci_set_drvdata(pdev, (void *)board_dat); + dev_dbg(&pdev->dev, "%s invoked pci_set_drvdata\n", __func__); + + /* set master mode */ + pch_spi_set_master_mode(master); + dev_dbg(&pdev->dev, + "%s invoked pch_spi_set_master_mode\n", __func__); + + /* Register the controller with the SPI core. */ + retval = spi_register_master(master); + if (retval != 0) { + dev_err(&pdev->dev, + "%s spi_register_master FAILED\n", __func__); + goto err_spi_reg_master; + } + + dev_dbg(&pdev->dev, "%s spi_register_master returned=%d\n", + __func__, retval); + + + return 0; + +err_spi_reg_master: + spi_unregister_master(master); +err_spi_get_resources: +err_spi_alloc_master: + spi_master_put(master); + pci_disable_device(pdev); +err_pci_en_device: + kfree(board_dat); +err_kmalloc: + return retval; +} + +static void pch_spi_remove(struct pci_dev *pdev) +{ + struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "%s ENTRY\n", __func__); + + if (!board_dat) { + dev_err(&pdev->dev, + "%s pci_get_drvdata returned NULL\n", __func__); + return; + } + + /* check for any pending messages */ + if ((-EBUSY) == pch_spi_check_request_pending(board_dat)) { + dev_dbg(&pdev->dev, + "%s pch_spi_check_request_pending returned" + " EBUSY\n", __func__); + /* no need to take any particular action; proceed with remove + even though queue is not empty */ + } + + /* Free resources allocated for PCH SPI */ + pch_spi_free_resources(board_dat); + + /* Unregister SPI master */ + spi_unregister_master(board_dat->data->master); + + /* free memory for private data */ + kfree(board_dat); + + pci_set_drvdata(pdev, NULL); + + /* disable PCI device */ + pci_disable_device(pdev); + + dev_dbg(&pdev->dev, "%s invoked pci_disable_device\n", __func__); +} + +#ifdef CONFIG_PM +static int pch_spi_suspend(struct pci_dev *pdev, pm_message_t state) +{ + u8 count; + int retval; + + struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "%s ENTRY\n", __func__); + + if (!board_dat) { + dev_err(&pdev->dev, + "%s pci_get_drvdata returned NULL\n", __func__); + return -EFAULT; + } + + retval = 0; + board_dat->suspend_sts = true; + + /* check if the current message is processed: + Only after thats done the transfer will be suspended */ + count = 255; + while ((--count) > 0) { + if (!(board_dat->data->bcurrent_msg_processing)) { + dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_" + "msg_processing = false\n", __func__); + break; + } else { + dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_msg_" + "processing = true\n", __func__); + } + msleep(PCH_SLEEP_TIME); + } + + /* Free IRQ */ + if (board_dat->irq_reg_sts) { + /* disable all interrupts */ + pch_spi_disable_interrupts(board_dat->data->master, PCH_ALL); + pch_spi_reset(board_dat->data->master); + dev_dbg(&pdev->dev, + "%s pch_spi_disable_interrupts invoked successfully\n", + __func__); + + free_irq(board_dat->pdev->irq, (void *)board_dat); + + board_dat->irq_reg_sts = false; + dev_dbg(&pdev->dev, + "%s free_irq invoked successfully.\n", __func__); + } + + /* save config space */ + retval = pci_save_state(pdev); + + if (retval == 0) { + dev_dbg(&pdev->dev, "%s pci_save_state returned=%d\n", + __func__, retval); + /* disable PM notifications */ + pci_enable_wake(pdev, PCI_D3hot, 0); + dev_dbg(&pdev->dev, + "%s pci_enable_wake invoked successfully\n", __func__); + /* disable PCI device */ + pci_disable_device(pdev); + dev_dbg(&pdev->dev, + "%s pci_disable_device invoked successfully\n", + __func__); + /* move device to D3hot state */ + pci_set_power_state(pdev, PCI_D3hot); + dev_dbg(&pdev->dev, + "%s pci_set_power_state invoked successfully\n", + __func__); + } else { + dev_err(&pdev->dev, "%s pci_save_state failed\n", __func__); + } + + dev_dbg(&pdev->dev, "%s return=%d\n", __func__, retval); + + return retval; +} + +static int pch_spi_resume(struct pci_dev *pdev) +{ + int retval; + + struct pch_spi_board_data *board = pci_get_drvdata(pdev); + dev_dbg(&pdev->dev, "%s ENTRY\n", __func__); + + if (!board) { + dev_err(&pdev->dev, + "%s pci_get_drvdata returned NULL\n", __func__); + return -EFAULT; + } + + /* move device to DO power state */ + pci_set_power_state(pdev, PCI_D0); + + /* restore state */ + pci_restore_state(pdev); + + retval = pci_enable_device(pdev); + if (retval < 0) { + dev_err(&pdev->dev, + "%s pci_enable_device failed\n", __func__); + } else { + /* disable PM notifications */ + pci_enable_wake(pdev, PCI_D3hot, 0); + + /* register IRQ handler */ + if (!(board->irq_reg_sts)) { + /* register IRQ */ + retval = request_irq(board->pdev->irq, pch_spi_handler, + IRQF_SHARED, KBUILD_MODNAME, + board); + if (retval < 0) { + dev_err(&pdev->dev, + "%s request_irq failed\n", __func__); + return retval; + } + board->irq_reg_sts = true; + + /* reset PCH SPI h/w */ + pch_spi_reset(board->data->master); + pch_spi_set_master_mode(board->data->master); + + /* set suspend status to false */ + board->suspend_sts = false; + + } + } + + dev_dbg(&pdev->dev, "%s returning=%d\n", __func__, retval); + + return retval; +} +#else +#define pch_spi_suspend NULL +#define pch_spi_resume NULL + +#endif + +static struct pci_driver pch_spi_pcidev = { + .name = "pch_spi", + .id_table = pch_spi_pcidev_id, + .probe = pch_spi_probe, + .remove = pch_spi_remove, + .suspend = pch_spi_suspend, + .resume = pch_spi_resume, +}; + +static int __init pch_spi_init(void) +{ + return pci_register_driver(&pch_spi_pcidev); +} +module_init(pch_spi_init); + + +static void __exit pch_spi_exit(void) +{ + pci_unregister_driver(&pch_spi_pcidev); +} +module_exit(pch_spi_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PCH SPI PCI Driver"); -- cgit v1.2.2 From 6ff554e06869e970e6ef2c4d44ea43315917d22c Mon Sep 17 00:00:00 2001 From: David Jander Date: Fri, 8 Oct 2010 11:24:01 +0200 Subject: spi/imx: Support different fifo sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The i.MX51 ECSPI has a fifo size of 64 entries instead of 8 entries as found on the other cspi bus devices. Cc: Jason Wang Signed-off-by: David Jander Signed-off-by: Sascha Hauer Signed-off-by: Uwe Kleine-König Acked-by: Grant Likely Signed-off-by: Sascha Hauer --- drivers/spi/spi_imx.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 6bab2cfd93c1..55a38e2c6c13 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -77,6 +77,7 @@ struct spi_imx_devtype_data { void (*trigger)(struct spi_imx_data *); int (*rx_available)(struct spi_imx_data *); void (*reset)(struct spi_imx_data *); + unsigned int fifosize; }; struct spi_imx_data { @@ -541,6 +542,7 @@ static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { .trigger = mx1_trigger, .rx_available = mx1_rx_available, .reset = mx1_reset, + .fifosize = 8, }, #endif #ifdef CONFIG_SPI_IMX_VER_0_0 @@ -550,6 +552,7 @@ static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { .trigger = mx27_trigger, .rx_available = mx27_rx_available, .reset = spi_imx0_0_reset, + .fifosize = 8, }, #endif #ifdef CONFIG_SPI_IMX_VER_0_4 @@ -559,6 +562,7 @@ static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { .trigger = mx31_trigger, .rx_available = mx31_rx_available, .reset = spi_imx0_4_reset, + .fifosize = 8, }, #endif #ifdef CONFIG_SPI_IMX_VER_0_7 @@ -568,6 +572,7 @@ static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { .trigger = mx31_trigger, .rx_available = mx31_rx_available, .reset = spi_imx0_4_reset, + .fifosize = 8, }, #endif #ifdef CONFIG_SPI_IMX_VER_2_3 @@ -577,6 +582,7 @@ static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { .trigger = spi_imx2_3_trigger, .rx_available = spi_imx2_3_rx_available, .reset = spi_imx2_3_reset, + .fifosize = 64, }, #endif }; @@ -596,7 +602,7 @@ static void spi_imx_chipselect(struct spi_device *spi, int is_active) static void spi_imx_push(struct spi_imx_data *spi_imx) { - while (spi_imx->txfifo < 8) { + while (spi_imx->txfifo < spi_imx->devtype_data.fifosize) { if (!spi_imx->count) break; spi_imx->tx(spi_imx); -- cgit v1.2.2 From 65308c46b760bb2ccb043b47bb5f053dbb8d11b5 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 29 Sep 2010 17:31:34 +0900 Subject: spi/topcliff: cleanups for style and conciseness This patch makes multiple cleanups to the new topcliff pch spi driver including, but not limited to, - removing superfluous brackets around variables - open coding functions that are only used once - removing unnecessary line breaks - removing unused functions - simplifying the interrupt enable/disable code - remove unnecessary (void *) casts. - remove b_mem_fail from pch_spi_set_tx to code it more cleanly - shorten dev_dbg() messages for conciseness and readability More cleanups are still needed in this driver. In particular, - the driver filename should be changed to spi_topcliff_pch.c - many of the dev_dbg() lines should be trimmed (particularly the ones on unconditional code paths). - I suspect that the locking model not correct. I'd like to know what drivers' critical regions are, and how they are protected. - get_resources and release_resources probably should be open coded in .probe and .release respectively. Signed-off-by: Grant Likely --- drivers/spi/spi_topcliff_pch.c | 502 +++++++++++------------------------------ 1 file changed, 138 insertions(+), 364 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c index 58b183f6eec0..97746232741e 100644 --- a/drivers/spi/spi_topcliff_pch.c +++ b/drivers/spi/spi_topcliff_pch.c @@ -1,8 +1,6 @@ /* * SPI bus driver for the Topcliff PCH used by Intel SoCs - */ - -/* + * * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. * * This program is free software; you can redistribute it and/or modify @@ -19,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include #include @@ -45,17 +44,7 @@ #define PCH_RX_THOLD 7 #define PCH_RX_THOLD_MAX 15 -#define PCH_RX 1 -#define PCH_TX 2 - -/* various interrupts */ -#define PCH_TFI 0x1 -#define PCH_RFI 0x2 -#define PCH_FI 0x4 -#define PCH_ORI 0x8 -#define PCH_MDFI 0x10 -#define PCH_ALL (PCH_TFI|PCH_RFI|PCH_FI|PCH_ORI|PCH_MDFI) #define PCH_MAX_BAUDRATE 5000000 #define PCH_MAX_FIFO_DEPTH 16 @@ -86,6 +75,8 @@ #define SPSR_FI_BIT (1 << 2) #define SPBRR_SIZE_BIT (1 << 10) +#define PCH_ALL (SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|SPCR_ORIE_BIT|SPCR_MDFIE_BIT) + #define SPCR_RFIC_FIELD 20 #define SPCR_TFIC_FIELD 16 @@ -176,16 +167,6 @@ static struct pci_device_id pch_spi_pcidev_id[] = { {0,} }; -static inline void pch_set_bitmsk(u32 *var, u32 bitmask) -{ - *var |= bitmask; -} - -static inline void pch_clr_bitmsk(u32 *var, u32 bitmask) -{ - *var &= (~(bitmask)); -} - /** * pch_spi_writereg() - Performs register writes * @master: Pointer to struct spi_master. @@ -194,9 +175,7 @@ static inline void pch_clr_bitmsk(u32 *var, u32 bitmask) */ static inline void pch_spi_writereg(struct spi_master *master, int idx, u32 val) { - struct pch_spi_data *data = spi_master_get_devdata(master); - iowrite32(val, (data->io_remap_addr + idx)); } @@ -208,19 +187,9 @@ static inline void pch_spi_writereg(struct spi_master *master, int idx, u32 val) static inline u32 pch_spi_readreg(struct spi_master *master, int idx) { struct pch_spi_data *data = spi_master_get_devdata(master); - return ioread32(data->io_remap_addr + idx); } -/* ope==true:Set bit, ope==false:Clear bit */ -static inline void pch_spi_setclr_bit(u32 *val, u32 pos, bool ope) -{ - if (ope) - *val |= pos; - else - *val &= (~(pos)); -} - static inline void pch_spi_setclr_reg(struct spi_master *master, int idx, u32 set, u32 clr) { @@ -229,7 +198,6 @@ static inline void pch_spi_setclr_reg(struct spi_master *master, int idx, pch_spi_writereg(master, idx, tmp); } - static void pch_spi_set_master_mode(struct spi_master *master) { pch_spi_setclr_reg(master, PCH_SPCR, SPCR_MSTR_BIT, 0); @@ -245,35 +213,6 @@ static void pch_spi_clear_fifo(struct spi_master *master) pch_spi_setclr_reg(master, PCH_SPCR, 0, SPCR_FICLR_BIT); } -/** - * ch_spi_disable_interrupts() - Disables specified interrupts - * @master: Pointer to struct spi_master. - * @interrupt: Interrups to be enabled. - */ -static void pch_spi_disable_interrupts(struct spi_master *master, u8 interrupt) -{ - u32 clr_flags = 0; - - if (interrupt & PCH_RFI) - clr_flags |= SPCR_RFIE_BIT; - - if (interrupt & PCH_TFI) - clr_flags |= SPCR_TFIE_BIT; - - if (interrupt & PCH_FI) - clr_flags |= SPCR_FIE_BIT; - - if (interrupt & PCH_ORI) - clr_flags |= SPCR_ORIE_BIT; - - if (interrupt & PCH_MDFI) - clr_flags |= SPCR_MDFIE_BIT; - - pch_spi_setclr_reg(master, PCH_SPCR, 0, clr_flags); - - dev_dbg(&master->dev, "%s clearing bits =%x\n", __func__, clr_flags); -} - static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, void __iomem *io_remap_addr) { @@ -309,9 +248,7 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, /* disable RFI if not needed */ if ((bpw_len - rx_index) <= PCH_MAX_FIFO_DEPTH) { reg_spcr_val = ioread32(io_remap_addr + PCH_SPCR); - - /* disable RFI */ - pch_clr_bitmsk(®_spcr_val, SPCR_RFIE_BIT); + reg_spcr_val &= ~SPCR_RFIE_BIT; /* disable RFI */ /* reset rx threshold */ reg_spcr_val &= MASK_RFIC_SPCR_BITS; @@ -329,7 +266,8 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, /* if transfer complete interrupt */ if (reg_spsr_val & SPSR_FI_BIT) { /* disable FI & RFI interrupts */ - pch_spi_disable_interrupts(data->master, PCH_FI | PCH_RFI); + pch_spi_setclr_reg(data->master, PCH_SPCR, 0, + SPCR_FIE_BIT | SPCR_TFIE_BIT); /* transfer is completed;inform pch_spi_process_messages */ data->transfer_complete = true; @@ -337,7 +275,6 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, } } - /** * pch_spi_handler() - Interrupt handler * @irq: The interrupt number. @@ -350,7 +287,6 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id) void __iomem *spsr; void __iomem *io_remap_addr; irqreturn_t ret = IRQ_NONE; - struct pch_spi_board_data *board_dat = dev_id; if (board_dat->suspend_sts) { @@ -366,7 +302,6 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id) reg_spsr_val = ioread32(spsr); /* Check if the interrupt is for SPI device */ - if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) { pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr); ret = IRQ_HANDLED; @@ -385,12 +320,9 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id) */ static void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz) { - u32 n_spbr; - - n_spbr = PCH_CLOCK_HZ / (speed_hz * 2); + u32 n_spbr = PCH_CLOCK_HZ / (speed_hz * 2); /* if baud rate is less than we can support limit it */ - if (n_spbr > PCH_MAX_SPBR) n_spbr = PCH_MAX_SPBR; @@ -417,105 +349,29 @@ static void pch_spi_set_bits_per_word(struct spi_master *master, */ static void pch_spi_setup_transfer(struct spi_device *spi) { - u32 reg_spcr_val; + u32 flags = 0; dev_dbg(&spi->dev, "%s SPBRR content =%x setting baud rate=%d\n", __func__, pch_spi_readreg(spi->master, PCH_SPBRR), spi->max_speed_hz); - pch_spi_set_baud_rate(spi->master, spi->max_speed_hz); /* set bits per word */ pch_spi_set_bits_per_word(spi->master, spi->bits_per_word); - if (spi->mode & SPI_LSB_FIRST) - pch_spi_setclr_reg(spi->master, PCH_SPCR, 0, SPCR_LSBF_BIT); - else - pch_spi_setclr_reg(spi->master, PCH_SPCR, SPCR_LSBF_BIT, 0); - + if (!(spi->mode & SPI_LSB_FIRST)) + flags |= SPCR_LSBF_BIT; if (spi->mode & SPI_CPOL) - pch_spi_setclr_reg(spi->master, PCH_SPCR, SPCR_CPOL_BIT, 0); - else - pch_spi_setclr_reg(spi->master, PCH_SPCR, 0, SPCR_CPOL_BIT); - + flags |= SPCR_CPOL_BIT; if (spi->mode & SPI_CPHA) - pch_spi_setclr_reg(spi->master, PCH_SPCR, SPCR_CPHA_BIT, 0); - else - pch_spi_setclr_reg(spi->master, PCH_SPCR, 0, SPCR_CPHA_BIT); - - dev_dbg(&spi->dev, - "%s SPCR content after setting LSB/MSB and MODE= %x\n", - __func__, reg_spcr_val); + flags |= SPCR_CPHA_BIT; + pch_spi_setclr_reg(spi->master, PCH_SPCR, flags, + (SPCR_LSBF_BIT | SPCR_CPOL_BIT | SPCR_CPHA_BIT)); /* Clear the FIFO by toggling FICLR to 1 and back to 0 */ pch_spi_clear_fifo(spi->master); } -/** - * pch_spi_enable_interrupts() - Enables specified interrupts - * @master: Pointer to struct spi_master. - * @interrupt: Interrups to be enabled. - */ -static void pch_spi_enable_interrupts(struct spi_master *master, u8 interrupt) -{ - u32 reg_val_spcr; - - dev_dbg(&master->dev, "%s SPCR content=%x\n", __func__, reg_val_spcr); - - if (interrupt & PCH_RFI) { - /* set RFIE bit in SPCR */ - dev_dbg(&master->dev, "setting RFI in %s\n", __func__); - pch_spi_setclr_reg(master, PCH_SPCR, SPCR_RFIE_BIT, 0); - } - - if (interrupt & PCH_TFI) { - /* set TFIE bit in SPCR */ - dev_dbg(&master->dev, "setting TFI in %s\n", __func__); - pch_spi_setclr_reg(master, PCH_SPCR, SPCR_TFIE_BIT, 0); - } - - if (interrupt & PCH_FI) { - /* set FIE bit in SPCR */ - dev_dbg(&master->dev, "setting FI in %s\n", __func__); - pch_spi_setclr_reg(master, PCH_SPCR, SPCR_FIE_BIT, 0); - } - - if (interrupt & PCH_ORI) { - /* set ORIE bit in SPCR */ - dev_dbg(&master->dev, "setting ORI in %s\n", __func__); - pch_spi_setclr_reg(master, PCH_SPCR, SPCR_ORIE_BIT, 0); - } - - if (interrupt & PCH_MDFI) { - /* set MODFIE bit in SPCR */ - dev_dbg(&master->dev, "setting MDFI in %s\n", __func__); - pch_spi_setclr_reg(master, PCH_SPCR, SPCR_MDFIE_BIT, 0); - } -} - -/** - * pch_spi_set_threshold() - Sets Tx/Rx FIFO thresholds - * @spi: Pointer to struct spi_device. - * @threshold: Threshold value to be set. - * @dir: Rx or Tx threshold to be set. - */ -static void pch_spi_set_threshold(struct spi_device *spi, u32 threshold, u8 dir) -{ - - if (dir == PCH_RX) { - dev_dbg(&spi->dev, "%s setting Rx threshold\n", __func__); - pch_spi_setclr_reg(spi->master, PCH_SPCR, - threshold << SPCR_RFIC_FIELD, - ~MASK_RFIC_SPCR_BITS); - - } else if (dir == PCH_TX) { - dev_dbg(&spi->dev, "%s setting Tx threshold\n", __func__); - pch_spi_setclr_reg(spi->master, PCH_SPCR, - (threshold << SPCR_TFIC_FIELD) , - ~MASK_TFIC_SPCR_BITS); - } -} - /** * pch_spi_reset() - Clears SPI registers * @master: Pointer to struct spi_master. @@ -532,13 +388,12 @@ static void pch_spi_reset(struct spi_master *master) static int pch_spi_setup(struct spi_device *pspi) { /* check bits per word */ - if ((pspi->bits_per_word) == 0) { + if (pspi->bits_per_word == 0) { pspi->bits_per_word = 8; dev_dbg(&pspi->dev, "%s 8 bits per word\n", __func__); } - if (((pspi->bits_per_word) != 8) && - ((pspi->bits_per_word != 16))) { + if ((pspi->bits_per_word != 8) && (pspi->bits_per_word != 16)) { dev_err(&pspi->dev, "%s Invalid bits per word\n", __func__); return -EINVAL; } @@ -550,7 +405,7 @@ static int pch_spi_setup(struct spi_device *pspi) pspi->max_speed_hz = PCH_MAX_BAUDRATE; dev_dbg(&pspi->dev, "%s MODE = %x\n", __func__, - ((pspi->mode) & (SPI_CPOL | SPI_CPHA))); + (pspi->mode) & (SPI_CPOL | SPI_CPHA)); return 0; } @@ -564,17 +419,15 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) unsigned long flags; /* validate spi message and baud rate */ - if (unlikely((list_empty(&pmsg->transfers) == 1) || - (pspi->max_speed_hz == 0))) { - if (list_empty(&pmsg->transfers) == 1) - dev_err(&pspi->dev, "%s list empty\n", __func__); - - if ((pspi->max_speed_hz) == 0) { - dev_err(&pspi->dev, "%s pch_spi_tranfer maxspeed=%d\n", - __func__, pspi->max_speed_hz); - } - dev_err(&pspi->dev, "%s returning EINVAL\n", __func__); + if (unlikely(list_empty(&pmsg->transfers) == 1)) { + dev_err(&pspi->dev, "%s list empty\n", __func__); + retval = -EINVAL; + goto err_out; + } + if (unlikely(pspi->max_speed_hz == 0)) { + dev_err(&pspi->dev, "%s pch_spi_tranfer maxspeed=%d\n", + __func__, pspi->max_speed_hz); retval = -EINVAL; goto err_out; } @@ -582,32 +435,28 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) dev_dbg(&pspi->dev, "%s Transfer List not empty. " "Transfer Speed is set.\n", __func__); - spin_lock_irqsave(&data->lock, flags); - /* validate Tx/Rx buffers and Transfer length */ list_for_each_entry(transfer, &pmsg->transfers, transfer_list) { - if ((!(transfer->tx_buf)) && (!(transfer->rx_buf))) { + if (!transfer->tx_buf && !transfer->rx_buf) { dev_err(&pspi->dev, "%s Tx and Rx buffer NULL\n", __func__); retval = -EINVAL; - goto err_return_spinlock; + goto err_out; } - if (!(transfer->len)) { + if (!transfer->len) { dev_err(&pspi->dev, "%s Transfer length invalid\n", __func__); retval = -EINVAL; - goto err_return_spinlock; + goto err_out; } dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length" " valid\n", __func__); /* if baud rate hs been specified validate the same */ - if (transfer->speed_hz) { - if ((transfer->speed_hz) > PCH_MAX_BAUDRATE) - transfer->speed_hz = PCH_MAX_BAUDRATE; - } + if (transfer->speed_hz > PCH_MAX_BAUDRATE) + transfer->speed_hz = PCH_MAX_BAUDRATE; /* if bits per word has been specified validate the same */ if (transfer->bits_per_word) { @@ -616,14 +465,15 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) retval = -EINVAL; dev_err(&pspi->dev, "%s Invalid bits per word\n", __func__); - goto err_return_spinlock; + goto err_out; } } } - /* We won't process any messages if we have been asked to terminate */ + spin_lock_irqsave(&data->lock, flags); - if (STATUS_EXITING == (data->status)) { + /* We won't process any messages if we have been asked to terminate */ + if (data->status == STATUS_EXITING) { dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__); retval = -ESHUTDOWN; goto err_return_spinlock; @@ -631,27 +481,23 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) /* If suspended ,return -EINVAL */ if (data->board_dat->suspend_sts) { - dev_err(&pspi->dev, - "%s bSuspending= true returning EINVAL\n", __func__); + dev_err(&pspi->dev, "%s suspend; returning EINVAL\n", __func__); retval = -EINVAL; goto err_return_spinlock; } /* set status of message */ pmsg->actual_length = 0; - dev_dbg(&pspi->dev, "%s - pmsg->status =%d\n", __func__, pmsg->status); pmsg->status = -EINPROGRESS; /* add message to queue */ list_add_tail(&pmsg->queue, &data->queue); - dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__); /* schedule work queue to run */ queue_work(data->wk, &data->work); - dev_dbg(&pspi->dev, "%s - Invoked queue work\n", __func__); retval = 0; @@ -666,10 +512,9 @@ err_out: static inline void pch_spi_select_chip(struct pch_spi_data *data, struct spi_device *pspi) { - if ((data->current_chip) != NULL) { - if ((pspi->chip_select) != (data->n_curnt_chip)) { - dev_dbg(&pspi->dev, - "%s : different slave-Invoking\n", __func__); + if (data->current_chip != NULL) { + if (pspi->chip_select != data->n_curnt_chip) { + dev_dbg(&pspi->dev, "%s : different slave\n", __func__); data->current_chip = NULL; } } @@ -683,9 +528,8 @@ static inline void pch_spi_select_chip(struct pch_spi_data *data, } static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw, - struct spi_message **ppmsg) + struct spi_message **ppmsg) { - int b_mem_fail; int size; u32 n_writes; int j; @@ -697,20 +541,16 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw, /* set baud rate if needed */ if (data->cur_trans->speed_hz) { - dev_dbg(&data->master->dev, - "%s:pctrldatasetting baud rate\n", __func__); - pch_spi_set_baud_rate(data->master, - (data->cur_trans->speed_hz)); + dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__); + pch_spi_set_baud_rate(data->master, data->cur_trans->speed_hz); } /* set bits per word if needed */ - if ((data->cur_trans->bits_per_word) && - ((data->current_msg->spi->bits_per_word) != - (data->cur_trans->bits_per_word))) { - dev_dbg(&data->master->dev, - "%s:setting bits per word\n", __func__); + if (data->cur_trans->bits_per_word && + (data->current_msg->spi->bits_per_word != data->cur_trans->bits_per_word)) { + dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__); pch_spi_set_bits_per_word(data->master, - (data->cur_trans->bits_per_word)); + data->cur_trans->bits_per_word); *bpw = data->cur_trans->bits_per_word; } else { *bpw = data->current_msg->spi->bits_per_word; @@ -721,28 +561,21 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw, data->rx_index = 0; data->bpw_len = data->cur_trans->len / (*bpw / 8); - b_mem_fail = false; /* find alloc size */ - size = (data->cur_trans->len) * (sizeof(*(data->pkt_tx_buff))); + size = data->cur_trans->len * sizeof(*data->pkt_tx_buff); + /* allocate memory for pkt_tx_buff & pkt_rx_buffer */ data->pkt_tx_buff = kzalloc(size, GFP_KERNEL); - if (data->pkt_tx_buff != NULL) { data->pkt_rx_buff = kzalloc(size, GFP_KERNEL); - - if (data->pkt_rx_buff == NULL) { - b_mem_fail = true; + if (!data->pkt_rx_buff) kfree(data->pkt_tx_buff); - } - } else { - b_mem_fail = true; } - if (b_mem_fail) { + if (!data->pkt_rx_buff) { /* flush queue and set status of all transfers to -ENOMEM */ - dev_err(&data->master->dev, - "Kzalloc fail in %s messages\n", __func__); + dev_err(&data->master->dev, "%s :kzalloc failed\n", __func__); list_for_each_entry(pmsg, data->queue.next, queue) { pmsg->status = -ENOMEM; @@ -752,39 +585,33 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw, /* delete from queue */ list_del_init(&pmsg->queue); } - return; } /* copy Tx Data */ - if ((data->cur_trans->tx_buf) != NULL) { + if (data->cur_trans->tx_buf != NULL) { if (*bpw == 8) { - for (j = 0; j < (data->bpw_len); j++) { - tx_buf = data->cur_trans->tx_buf; - data->pkt_tx_buff[j] = tx_buf[j]; - } + tx_buf = data->cur_trans->tx_buf; + for (j = 0; j < data->bpw_len; j++) + data->pkt_tx_buff[j] = *tx_buf++; } else { - for (j = 0; j < (data->bpw_len); j++) { - tx_sbuf = data->cur_trans->tx_buf; - data->pkt_tx_buff[j] = tx_sbuf[j]; - } + tx_sbuf = data->cur_trans->tx_buf; + for (j = 0; j < data->bpw_len; j++) + data->pkt_tx_buff[j] = *tx_sbuf++; } } /* if len greater than PCH_MAX_FIFO_DEPTH, write 16,else len bytes */ - if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) + n_writes = data->bpw_len; + if (n_writes > PCH_MAX_FIFO_DEPTH) n_writes = PCH_MAX_FIFO_DEPTH; - else - n_writes = (data->bpw_len); - dev_dbg(&data->master->dev, "\n%s:Pulling down SSN low - writing " + dev_dbg(&data->master->dev, "\n%s:Pulling down SSN low - writing " "0x2 to SSNXCR\n", __func__); pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW); - for (j = 0; j < n_writes; j++) { - pch_spi_writereg(data->master, PCH_SPDWR, - data->pkt_tx_buff[j]); - } + for (j = 0; j < n_writes; j++) + pch_spi_writereg(data->master, PCH_SPDWR, data->pkt_tx_buff[j]); /* update tx_index */ data->tx_index = j; @@ -798,13 +625,12 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw, static void pch_spi_nomore_transfer(struct pch_spi_data *data, struct spi_message *pmsg) { - dev_dbg(&data->master->dev, - "%s:no more transfers in this message\n", __func__); + dev_dbg(&data->master->dev, "%s called\n", __func__); /* Invoke complete callback - [To the spi core..indicating end of transfer] */ + * [To the spi core..indicating end of transfer] */ data->current_msg->status = 0; - if ((data->current_msg->complete) != 0) { + if (data->current_msg->complete != 0) { dev_dbg(&data->master->dev, "%s:Invoking callback of SPI core\n", __func__); data->current_msg->complete(data->current_msg->context); @@ -819,28 +645,26 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data, data->current_msg = NULL; data->cur_trans = NULL; - /* check if we have items in list and not suspending */ - /* return 1 if list empty */ + /* check if we have items in list and not suspending + * return 1 if list empty */ if ((list_empty(&data->queue) == 0) && - (!(data->board_dat->suspend_sts)) - && (data->status != STATUS_EXITING)) { + (!data->board_dat->suspend_sts) && + (data->status != STATUS_EXITING)) { /* We have some more work to do (either there is more tranint - bpw;sfer requests in the current message or there are - more messages) - */ - dev_dbg(&data->master->dev, - "%s:we have pending messages-Invoking queue_work\n", - __func__); + * bpw;sfer requests in the current message or there are + *more messages) + */ + dev_dbg(&data->master->dev, "%s:Invoke queue_work\n", __func__); queue_work(data->wk, &data->work); - } else if ((data->board_dat->suspend_sts) || - (data->status == STATUS_EXITING)) { + } else if (data->board_dat->suspend_sts || + data->status == STATUS_EXITING) { dev_dbg(&data->master->dev, "%s suspend/remove initiated, flushing queue\n", __func__); list_for_each_entry(pmsg, data->queue.next, queue) { pmsg->status = -EIO; - if (pmsg->complete != 0) + if (pmsg->complete) pmsg->complete(pmsg->context); /* delete from queue */ @@ -851,30 +675,29 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data, static void pch_spi_set_ir(struct pch_spi_data *data) { - u32 reg_spcr_val; - /* enable interrupts */ if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) { /* set receive threhold to PCH_RX_THOLD */ - pch_spi_set_threshold(data->current_chip, PCH_RX_THOLD, PCH_RX); + pch_spi_setclr_reg(data->master, PCH_SPCR, + PCH_RX_THOLD << SPCR_TFIC_FIELD, + ~MASK_TFIC_SPCR_BITS); /* enable FI and RFI interrupts */ - pch_spi_enable_interrupts(data->master, PCH_RFI | PCH_FI); + pch_spi_setclr_reg(data->master, PCH_SPCR, + SPCR_RFIE_BIT | SPCR_TFIE_BIT, 0); } else { /* set receive threhold to maximum */ - pch_spi_set_threshold(data->current_chip, PCH_RX_THOLD_MAX, - PCH_RX); + pch_spi_setclr_reg(data->master, PCH_SPCR, + PCH_RX_THOLD_MAX << SPCR_TFIC_FIELD, + ~MASK_TFIC_SPCR_BITS); /* enable FI interrupt */ - pch_spi_enable_interrupts(data->master, PCH_FI); + pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_FIE_BIT, 0); } dev_dbg(&data->master->dev, "%s:invoking pch_spi_set_enable to enable SPI\n", __func__); /* SPI set enable */ - reg_spcr_val = pch_spi_readreg(data->current_chip->master, PCH_SPCR); - pch_set_bitmsk(®_spcr_val, SPCR_SPE_BIT); - pch_spi_writereg(data->current_chip->master, PCH_SPCR, reg_spcr_val); - + pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT, 0); /* Wait until the transfer completes; go to sleep after initiating the transfer. */ @@ -893,9 +716,9 @@ static void pch_spi_set_ir(struct pch_spi_data *data) /* clear all interrupts */ pch_spi_writereg(data->master, PCH_SPSR, - (pch_spi_readreg(data->master, PCH_SPSR))); + pch_spi_readreg(data->master, PCH_SPSR)); /* disable interrupts */ - pch_spi_disable_interrupts(data->master, PCH_ALL); + pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL); } static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw) @@ -905,19 +728,17 @@ static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw) u16 *rx_sbuf; /* copy Rx Data */ - if (!(data->cur_trans->rx_buf)) + if (!data->cur_trans->rx_buf) return; if (bpw == 8) { - for (j = 0; j < (data->bpw_len); j++) { - rx_buf = data->cur_trans->rx_buf; - rx_buf[j] = (data->pkt_rx_buff[j]) & 0xFF; - } + rx_buf = data->cur_trans->rx_buf; + for (j = 0; j < data->bpw_len; j++) + *rx_buf++ = data->pkt_rx_buff[j] & 0xFF; } else { - for (j = 0; j < (data->bpw_len); j++) { - rx_sbuf = data->cur_trans->rx_buf; - rx_sbuf[j] = data->pkt_rx_buff[j]; - } + rx_sbuf = data->cur_trans->rx_buf; + for (j = 0; j < data->bpw_len; j++) + *rx_sbuf++ = data->pkt_rx_buff[j]; } } @@ -925,20 +746,20 @@ static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw) static void pch_spi_process_messages(struct work_struct *pwork) { struct spi_message *pmsg; + struct pch_spi_data *data; int bpw; - struct pch_spi_data *data = - container_of(pwork, struct pch_spi_data, work); dev_dbg(&data->master->dev, "%s data initialized\n", __func__); + data = container_of(pwork, struct pch_spi_data, work); spin_lock(&data->lock); /* check if suspend has been initiated;if yes flush queue */ - if ((data->board_dat->suspend_sts) || - (data->status == STATUS_EXITING)) { + if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) { dev_dbg(&data->master->dev, "%s suspend/remove initiated,flushing queue\n", __func__); + list_for_each_entry(pmsg, data->queue.next, queue) { pmsg->status = -EIO; @@ -961,8 +782,8 @@ static void pch_spi_process_messages(struct work_struct *pwork) "%s Set data->bcurrent_msg_processing= true\n", __func__); /* Get the message from the queue and delete it from there. */ - data->current_msg = - list_entry(data->queue.next, struct spi_message, queue); + data->current_msg = list_entry(data->queue.next, struct spi_message, + queue); list_del_init(&data->current_msg->queue); @@ -1044,7 +865,7 @@ static void pch_spi_process_messages(struct work_struct *pwork) spin_unlock(&data->lock); - } while ((data->cur_trans) != NULL); + } while (data->cur_trans != NULL); } static void pch_spi_free_resources(struct pch_spi_board_data *board_dat) @@ -1063,14 +884,11 @@ static void pch_spi_free_resources(struct pch_spi_board_data *board_dat) /* disable interrupts & free IRQ */ if (board_dat->irq_reg_sts) { /* disable interrupts */ - pch_spi_disable_interrupts(board_dat->data-> - master, PCH_ALL); - dev_dbg(&board_dat->pdev->dev, - "%s pch_spi_disable_interrupts invoked " - "successfully\n", __func__); + pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0, + PCH_ALL); /* free IRQ */ - free_irq(board_dat->pdev->irq, (void *)board_dat); + free_irq(board_dat->pdev->irq, board_dat); dev_dbg(&board_dat->pdev->dev, "%s free_irq invoked successfully\n", __func__); @@ -1079,7 +897,7 @@ static void pch_spi_free_resources(struct pch_spi_board_data *board_dat) } /* unmap PCI base address */ - if ((board_dat->data->io_remap_addr) != 0) { + if (board_dat->data->io_remap_addr != 0) { pci_iounmap(board_dat->pdev, board_dat->data->io_remap_addr); board_dat->data->io_remap_addr = 0; @@ -1104,26 +922,9 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat) int retval; dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__); - /* iniatize queue of pending messages */ - INIT_LIST_HEAD(&(board_dat->data->queue)); - - /* initialize spin locks */ - spin_lock_init(&(board_dat->data->lock)); - - /* set channel status */ - board_dat->data->status = STATUS_RUNNING; - - /* initialize work structure */ - INIT_WORK(&(board_dat->data->work), - pch_spi_process_messages); - - /* initialize wait queues */ - init_waitqueue_head(&(board_dat->data->wait)); - /* create workqueue */ board_dat->data->wk = create_singlethread_workqueue(KBUILD_MODNAME); - - if ((board_dat->data->wk) == NULL) { + if (!board_dat->data->wk) { dev_err(&board_dat->pdev->dev, "%s create_singlet hread_workqueue failed\n", __func__); retval = -EBUSY; @@ -1143,7 +944,6 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat) board_dat->pci_req_sts = true; io_remap_addr = pci_iomap(board_dat->pdev, 1, 0); - if (io_remap_addr == 0) { dev_err(&board_dat->pdev->dev, "%s pci_iomap failed\n", __func__); @@ -1161,7 +961,7 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat) /* register IRQ */ retval = request_irq(board_dat->pdev->irq, pch_spi_handler, - IRQF_SHARED, KBUILD_MODNAME, (void *)board_dat); + IRQF_SHARED, KBUILD_MODNAME, board_dat); if (retval != 0) { dev_err(&board_dat->pdev->dev, "%s request_irq failed\n", __func__); @@ -1172,8 +972,7 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat) __func__, retval); board_dat->irq_reg_sts = true; - dev_dbg(&board_dat->pdev->dev, - "%s data->irq_reg_sts=true\n", __func__); + dev_dbg(&board_dat->pdev->dev, "%s data->irq_reg_sts=true\n", __func__); err_return: if (retval != 0) { @@ -1187,38 +986,6 @@ err_return: return retval; } -static int pch_spi_check_request_pending(struct pch_spi_board_data *board_dat) -{ - int sts; - u16 count; - - count = 500; - spin_lock(&(board_dat->data->lock)); - board_dat->data->status = STATUS_EXITING; - - while ((list_empty(&(board_dat->data->queue)) == 0) && - (--count)) { - dev_dbg(&board_dat->pdev->dev, - "%s :queue not empty\n", __func__); - spin_unlock(&(board_dat->data->lock)); - msleep(PCH_SLEEP_TIME); - spin_lock(&(board_dat->data->lock)); - } - - spin_unlock(&(board_dat->data->lock)); - - if (count) { - sts = 0; - dev_dbg(&board_dat->pdev->dev, "%s :queue empty\n", __func__); - } else { - sts = -EBUSY; - } - - dev_dbg(&board_dat->pdev->dev, "%s : EXIT=%d\n", __func__, sts); - - return sts; -} - static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1279,12 +1046,17 @@ static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id) board_dat->data->master = master; board_dat->data->n_curnt_chip = 255; board_dat->data->board_dat = board_dat; + board_dat->data->status = STATUS_RUNNING; + + INIT_LIST_HEAD(&board_dat->data->queue); + spin_lock_init(&board_dat->data->lock); + INIT_WORK(&board_dat->data->work, pch_spi_process_messages); + init_waitqueue_head(&board_dat->data->wait); /* allocate resources for PCH SPI */ retval = pch_spi_get_resources(board_dat); - if (retval != 0) { - dev_err(&pdev->dev, "%s fail(retval=%d)\n", - __func__, retval); + if (retval) { + dev_err(&pdev->dev, "%s fail(retval=%d)\n", __func__, retval); goto err_spi_get_resources; } @@ -1292,7 +1064,7 @@ static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id) __func__, retval); /* save private data in dev */ - pci_set_drvdata(pdev, (void *)board_dat); + pci_set_drvdata(pdev, board_dat); dev_dbg(&pdev->dev, "%s invoked pci_set_drvdata\n", __func__); /* set master mode */ @@ -1329,6 +1101,7 @@ err_kmalloc: static void pch_spi_remove(struct pci_dev *pdev) { struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev); + int count; dev_dbg(&pdev->dev, "%s ENTRY\n", __func__); @@ -1338,19 +1111,23 @@ static void pch_spi_remove(struct pci_dev *pdev) return; } - /* check for any pending messages */ - if ((-EBUSY) == pch_spi_check_request_pending(board_dat)) { - dev_dbg(&pdev->dev, - "%s pch_spi_check_request_pending returned" - " EBUSY\n", __func__); - /* no need to take any particular action; proceed with remove - even though queue is not empty */ + /* check for any pending messages; no action is taken if the queue + * is still full; but at least we tried. Unload anyway */ + count = 500; + spin_lock(&board_dat->data->lock); + board_dat->data->status = STATUS_EXITING; + while ((list_empty(&board_dat->data->queue) == 0) && --count) { + dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n", + __func__); + spin_unlock(&board_dat->data->lock); + msleep(PCH_SLEEP_TIME); + spin_lock(&board_dat->data->lock); } + spin_unlock(&board_dat->data->lock); /* Free resources allocated for PCH SPI */ pch_spi_free_resources(board_dat); - /* Unregister SPI master */ spi_unregister_master(board_dat->data->master); /* free memory for private data */ @@ -1401,13 +1178,11 @@ static int pch_spi_suspend(struct pci_dev *pdev, pm_message_t state) /* Free IRQ */ if (board_dat->irq_reg_sts) { /* disable all interrupts */ - pch_spi_disable_interrupts(board_dat->data->master, PCH_ALL); + pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0, + PCH_ALL); pch_spi_reset(board_dat->data->master); - dev_dbg(&pdev->dev, - "%s pch_spi_disable_interrupts invoked successfully\n", - __func__); - free_irq(board_dat->pdev->irq, (void *)board_dat); + free_irq(board_dat->pdev->irq, board_dat); board_dat->irq_reg_sts = false; dev_dbg(&pdev->dev, @@ -1471,7 +1246,7 @@ static int pch_spi_resume(struct pci_dev *pdev) pci_enable_wake(pdev, PCI_D3hot, 0); /* register IRQ handler */ - if (!(board->irq_reg_sts)) { + if (!board->irq_reg_sts) { /* register IRQ */ retval = request_irq(board->pdev->irq, pch_spi_handler, IRQF_SHARED, KBUILD_MODNAME, @@ -1518,7 +1293,6 @@ static int __init pch_spi_init(void) } module_init(pch_spi_init); - static void __exit pch_spi_exit(void) { pci_unregister_driver(&pch_spi_pcidev); @@ -1526,4 +1300,4 @@ static void __exit pch_spi_exit(void) module_exit(pch_spi_exit); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("PCH SPI PCI Driver"); +MODULE_DESCRIPTION("Topcliff PCH SPI PCI Driver"); -- cgit v1.2.2 From cdbc8f042f4f2568bb58ba8bd50d0692f3059417 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 8 Oct 2010 12:56:13 -0600 Subject: spi/topcliff: Tidy up Kconfig help text The original didn't specify Topcliff in the config prompt text. Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b32167a686bb..d966de1afc52 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -299,12 +299,11 @@ config SPI_STMP3XXX SPI driver for Freescale STMP37xx/378x SoC SSP interface config SPI_TOPCLIFF_PCH - tristate "PCH SPI Controller" + tristate "Topcliff PCH SPI Controller" depends on PCI help - This driver is for PCH(Platform controller Hub) SPI of Topcliff which - is an IOH(Input/Output Hub) for x86 embedded processor. - This driver can access PCH SPI bus device. + SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus + used in some x86 embedded processors. config SPI_TXX9 tristate "Toshiba TXx9 SPI controller" -- cgit v1.2.2 From b1b6b9aa6fd32db97469e65d301ebc32dcd67992 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Sep 2010 17:31:35 +0900 Subject: spi/pl022: add PrimeCell generic DMA support This extends the PL022 SSP/SPI driver with generic DMA engine support using the PrimeCell DMA engine interface. Also fix up the test code for the U300 platform. Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 516 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 428 insertions(+), 88 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 8cdddc97325c..db0c67908d2b 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -27,7 +27,6 @@ /* * TODO: * - add timeout on polled transfers - * - add generic DMA framework support */ #include @@ -45,6 +44,9 @@ #include #include #include +#include +#include +#include /* * This macro is used to define some register default values. @@ -381,6 +383,14 @@ struct pl022 { enum ssp_reading read; enum ssp_writing write; u32 exp_fifo_level; + /* DMA settings */ +#ifdef CONFIG_DMA_ENGINE + struct dma_chan *dma_rx_channel; + struct dma_chan *dma_tx_channel; + struct sg_table sgt_rx; + struct sg_table sgt_tx; + char *dummypage; +#endif }; /** @@ -406,7 +416,7 @@ struct chip_data { u16 dmacr; u16 cpsr; u8 n_bytes; - u8 enable_dma:1; + bool enable_dma; enum ssp_reading read; enum ssp_writing write; void (*cs_control) (u32 command); @@ -763,6 +773,371 @@ static void *next_transfer(struct pl022 *pl022) } return STATE_DONE; } + +/* + * This DMA functionality is only compiled in if we have + * access to the generic DMA devices/DMA engine. + */ +#ifdef CONFIG_DMA_ENGINE +static void unmap_free_dma_scatter(struct pl022 *pl022) +{ + /* Unmap and free the SG tables */ + dma_unmap_sg(&pl022->adev->dev, pl022->sgt_tx.sgl, + pl022->sgt_tx.nents, DMA_TO_DEVICE); + dma_unmap_sg(&pl022->adev->dev, pl022->sgt_rx.sgl, + pl022->sgt_rx.nents, DMA_FROM_DEVICE); + sg_free_table(&pl022->sgt_rx); + sg_free_table(&pl022->sgt_tx); +} + +static void dma_callback(void *data) +{ + struct pl022 *pl022 = data; + struct spi_message *msg = pl022->cur_msg; + + BUG_ON(!pl022->sgt_rx.sgl); + +#ifdef VERBOSE_DEBUG + /* + * Optionally dump out buffers to inspect contents, this is + * good if you want to convince yourself that the loopback + * read/write contents are the same, when adopting to a new + * DMA engine. + */ + { + struct scatterlist *sg; + unsigned int i; + + dma_sync_sg_for_cpu(&pl022->adev->dev, + pl022->sgt_rx.sgl, + pl022->sgt_rx.nents, + DMA_FROM_DEVICE); + + for_each_sg(pl022->sgt_rx.sgl, sg, pl022->sgt_rx.nents, i) { + dev_dbg(&pl022->adev->dev, "SPI RX SG ENTRY: %d", i); + print_hex_dump(KERN_ERR, "SPI RX: ", + DUMP_PREFIX_OFFSET, + 16, + 1, + sg_virt(sg), + sg_dma_len(sg), + 1); + } + for_each_sg(pl022->sgt_tx.sgl, sg, pl022->sgt_tx.nents, i) { + dev_dbg(&pl022->adev->dev, "SPI TX SG ENTRY: %d", i); + print_hex_dump(KERN_ERR, "SPI TX: ", + DUMP_PREFIX_OFFSET, + 16, + 1, + sg_virt(sg), + sg_dma_len(sg), + 1); + } + } +#endif + + unmap_free_dma_scatter(pl022); + + /* Update total bytes transfered */ + msg->actual_length += pl022->cur_transfer->len; + if (pl022->cur_transfer->cs_change) + pl022->cur_chip-> + cs_control(SSP_CHIP_DESELECT); + + /* Move to next transfer */ + msg->state = next_transfer(pl022); + tasklet_schedule(&pl022->pump_transfers); +} + +static void setup_dma_scatter(struct pl022 *pl022, + void *buffer, + unsigned int length, + struct sg_table *sgtab) +{ + struct scatterlist *sg; + int bytesleft = length; + void *bufp = buffer; + int mapbytes; + int i; + + if (buffer) { + for_each_sg(sgtab->sgl, sg, sgtab->nents, i) { + /* + * If there are less bytes left than what fits + * in the current page (plus page alignment offset) + * we just feed in this, else we stuff in as much + * as we can. + */ + if (bytesleft < (PAGE_SIZE - offset_in_page(bufp))) + mapbytes = bytesleft; + else + mapbytes = PAGE_SIZE - offset_in_page(bufp); + sg_set_page(sg, virt_to_page(bufp), + mapbytes, offset_in_page(bufp)); + bufp += mapbytes; + bytesleft -= mapbytes; + dev_dbg(&pl022->adev->dev, + "set RX/TX target page @ %p, %d bytes, %d left\n", + bufp, mapbytes, bytesleft); + } + } else { + /* Map the dummy buffer on every page */ + for_each_sg(sgtab->sgl, sg, sgtab->nents, i) { + if (bytesleft < PAGE_SIZE) + mapbytes = bytesleft; + else + mapbytes = PAGE_SIZE; + sg_set_page(sg, virt_to_page(pl022->dummypage), + mapbytes, 0); + bytesleft -= mapbytes; + dev_dbg(&pl022->adev->dev, + "set RX/TX to dummy page %d bytes, %d left\n", + mapbytes, bytesleft); + + } + } + BUG_ON(bytesleft); +} + +/** + * configure_dma - configures the channels for the next transfer + * @pl022: SSP driver's private data structure + */ +static int configure_dma(struct pl022 *pl022) +{ + struct dma_slave_config rx_conf = { + .src_addr = SSP_DR(pl022->phybase), + .direction = DMA_FROM_DEVICE, + .src_maxburst = pl022->vendor->fifodepth >> 1, + }; + struct dma_slave_config tx_conf = { + .dst_addr = SSP_DR(pl022->phybase), + .direction = DMA_TO_DEVICE, + .dst_maxburst = pl022->vendor->fifodepth >> 1, + }; + unsigned int pages; + int ret; + int sglen; + struct dma_chan *rxchan = pl022->dma_rx_channel; + struct dma_chan *txchan = pl022->dma_tx_channel; + struct dma_async_tx_descriptor *rxdesc; + struct dma_async_tx_descriptor *txdesc; + dma_cookie_t cookie; + + /* Check that the channels are available */ + if (!rxchan || !txchan) + return -ENODEV; + + switch (pl022->read) { + case READING_NULL: + /* Use the same as for writing */ + rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; + break; + case READING_U8: + rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case READING_U16: + rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case READING_U32: + rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + } + + switch (pl022->write) { + case WRITING_NULL: + /* Use the same as for reading */ + tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; + break; + case WRITING_U8: + tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case WRITING_U16: + tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case WRITING_U32: + tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;; + break; + } + + /* SPI pecularity: we need to read and write the same width */ + if (rx_conf.src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) + rx_conf.src_addr_width = tx_conf.dst_addr_width; + if (tx_conf.dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) + tx_conf.dst_addr_width = rx_conf.src_addr_width; + BUG_ON(rx_conf.src_addr_width != tx_conf.dst_addr_width); + + rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG, + (unsigned long) &rx_conf); + txchan->device->device_control(txchan, DMA_SLAVE_CONFIG, + (unsigned long) &tx_conf); + + /* Create sglists for the transfers */ + pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1; + dev_dbg(&pl022->adev->dev, "using %d pages for transfer\n", pages); + + ret = sg_alloc_table(&pl022->sgt_rx, pages, GFP_KERNEL); + if (ret) + goto err_alloc_rx_sg; + + ret = sg_alloc_table(&pl022->sgt_tx, pages, GFP_KERNEL); + if (ret) + goto err_alloc_tx_sg; + + /* Fill in the scatterlists for the RX+TX buffers */ + setup_dma_scatter(pl022, pl022->rx, + pl022->cur_transfer->len, &pl022->sgt_rx); + setup_dma_scatter(pl022, pl022->tx, + pl022->cur_transfer->len, &pl022->sgt_tx); + + /* Map DMA buffers */ + sglen = dma_map_sg(&pl022->adev->dev, pl022->sgt_rx.sgl, + pl022->sgt_rx.nents, DMA_FROM_DEVICE); + if (!sglen) + goto err_rx_sgmap; + + sglen = dma_map_sg(&pl022->adev->dev, pl022->sgt_tx.sgl, + pl022->sgt_tx.nents, DMA_TO_DEVICE); + if (!sglen) + goto err_tx_sgmap; + + /* Send both scatterlists */ + rxdesc = rxchan->device->device_prep_slave_sg(rxchan, + pl022->sgt_rx.sgl, + pl022->sgt_rx.nents, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!rxdesc) + goto err_rxdesc; + + txdesc = txchan->device->device_prep_slave_sg(txchan, + pl022->sgt_tx.sgl, + pl022->sgt_tx.nents, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) + goto err_txdesc; + + /* Put the callback on the RX transfer only, that should finish last */ + rxdesc->callback = dma_callback; + rxdesc->callback_param = pl022; + + /* Submit and fire RX and TX with TX last so we're ready to read! */ + cookie = rxdesc->tx_submit(rxdesc); + if (dma_submit_error(cookie)) + goto err_submit_rx; + cookie = txdesc->tx_submit(txdesc); + if (dma_submit_error(cookie)) + goto err_submit_tx; + rxchan->device->device_issue_pending(rxchan); + txchan->device->device_issue_pending(txchan); + + return 0; + +err_submit_tx: +err_submit_rx: +err_txdesc: + txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0); +err_rxdesc: + rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0); + dma_unmap_sg(&pl022->adev->dev, pl022->sgt_tx.sgl, + pl022->sgt_tx.nents, DMA_TO_DEVICE); +err_tx_sgmap: + dma_unmap_sg(&pl022->adev->dev, pl022->sgt_rx.sgl, + pl022->sgt_tx.nents, DMA_FROM_DEVICE); +err_rx_sgmap: + sg_free_table(&pl022->sgt_tx); +err_alloc_tx_sg: + sg_free_table(&pl022->sgt_rx); +err_alloc_rx_sg: + return -ENOMEM; +} + +static int __init pl022_dma_probe(struct pl022 *pl022) +{ + dma_cap_mask_t mask; + + /* Try to acquire a generic DMA engine slave channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + /* + * We need both RX and TX channels to do DMA, else do none + * of them. + */ + pl022->dma_rx_channel = dma_request_channel(mask, + pl022->master_info->dma_filter, + pl022->master_info->dma_rx_param); + if (!pl022->dma_rx_channel) { + dev_err(&pl022->adev->dev, "no RX DMA channel!\n"); + goto err_no_rxchan; + } + + pl022->dma_tx_channel = dma_request_channel(mask, + pl022->master_info->dma_filter, + pl022->master_info->dma_tx_param); + if (!pl022->dma_tx_channel) { + dev_err(&pl022->adev->dev, "no TX DMA channel!\n"); + goto err_no_txchan; + } + + pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!pl022->dummypage) { + dev_err(&pl022->adev->dev, "no DMA dummypage!\n"); + goto err_no_dummypage; + } + + dev_info(&pl022->adev->dev, "setup for DMA on RX %s, TX %s\n", + dma_chan_name(pl022->dma_rx_channel), + dma_chan_name(pl022->dma_tx_channel)); + + return 0; + +err_no_dummypage: + dma_release_channel(pl022->dma_tx_channel); +err_no_txchan: + dma_release_channel(pl022->dma_rx_channel); + pl022->dma_rx_channel = NULL; +err_no_rxchan: + return -ENODEV; +} + +static void terminate_dma(struct pl022 *pl022) +{ + struct dma_chan *rxchan = pl022->dma_rx_channel; + struct dma_chan *txchan = pl022->dma_tx_channel; + + rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0); + txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0); + unmap_free_dma_scatter(pl022); +} + +static void pl022_dma_remove(struct pl022 *pl022) +{ + if (pl022->busy) + terminate_dma(pl022); + if (pl022->dma_tx_channel) + dma_release_channel(pl022->dma_tx_channel); + if (pl022->dma_rx_channel) + dma_release_channel(pl022->dma_rx_channel); + kfree(pl022->dummypage); +} + +#else +static inline int configure_dma(struct pl022 *pl022) +{ + return -ENODEV; +} + +static inline int pl022_dma_probe(struct pl022 *pl022) +{ + return 0; +} + +static inline void pl022_dma_remove(struct pl022 *pl022) +{ +} +#endif + /** * pl022_interrupt_handler - Interrupt handler for SSP controller * @@ -794,14 +1169,17 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id) if (unlikely(!irq_status)) return IRQ_NONE; - /* This handles the error code interrupts */ + /* + * This handles the FIFO interrupts, the timeout + * interrupts are flatly ignored, they cannot be + * trusted. + */ if (unlikely(irq_status & SSP_MIS_MASK_RORMIS)) { /* * Overrun interrupt - bail out since our Data has been * corrupted */ - dev_err(&pl022->adev->dev, - "FIFO overrun\n"); + dev_err(&pl022->adev->dev, "FIFO overrun\n"); if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RFF) dev_err(&pl022->adev->dev, "RXFIFO is full\n"); @@ -896,8 +1274,8 @@ static int set_up_next_transfer(struct pl022 *pl022, } /** - * pump_transfers - Tasklet function which schedules next interrupt transfer - * when running in interrupt transfer mode. + * pump_transfers - Tasklet function which schedules next transfer + * when running in interrupt or DMA transfer mode. * @data: SSP driver private data structure * */ @@ -954,65 +1332,23 @@ static void pump_transfers(unsigned long data) } /* Flush the FIFOs and let's go! */ flush(pl022); - writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase)); -} - -/** - * NOT IMPLEMENTED - * configure_dma - It configures the DMA pipes for DMA transfers - * @data: SSP driver's private data structure - * - */ -static int configure_dma(void *data) -{ - struct pl022 *pl022 = data; - dev_dbg(&pl022->adev->dev, "configure DMA\n"); - return -ENOTSUPP; -} - -/** - * do_dma_transfer - It handles transfers of the current message - * if it is DMA xfer. - * NOT FULLY IMPLEMENTED - * @data: SSP driver's private data structure - */ -static void do_dma_transfer(void *data) -{ - struct pl022 *pl022 = data; - - if (configure_dma(data)) { - dev_dbg(&pl022->adev->dev, "configuration of DMA Failed!\n"); - goto err_config_dma; - } - /* TODO: Implememt DMA setup of pipes here */ - - /* Enable target chip, set up transfer */ - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); - if (set_up_next_transfer(pl022, pl022->cur_transfer)) { - /* Error path */ - pl022->cur_msg->state = STATE_ERROR; - pl022->cur_msg->status = -EIO; - giveback(pl022); + if (pl022->cur_chip->enable_dma) { + if (configure_dma(pl022)) { + dev_dbg(&pl022->adev->dev, + "configuration of DMA failed, fall back to interrupt mode\n"); + goto err_config_dma; + } return; } - /* Enable SSP */ - writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE), - SSP_CR1(pl022->virtbase)); - - /* TODO: Enable the DMA transfer here */ - return; - err_config_dma: - pl022->cur_msg->state = STATE_ERROR; - pl022->cur_msg->status = -EIO; - giveback(pl022); - return; +err_config_dma: + writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase)); } -static void do_interrupt_transfer(void *data) +static void do_interrupt_dma_transfer(struct pl022 *pl022) { - struct pl022 *pl022 = data; + u32 irqflags = ENABLE_ALL_INTERRUPTS; /* Enable target chip */ pl022->cur_chip->cs_control(SSP_CHIP_SELECT); @@ -1023,15 +1359,26 @@ static void do_interrupt_transfer(void *data) giveback(pl022); return; } + /* If we're using DMA, set up DMA here */ + if (pl022->cur_chip->enable_dma) { + /* Configure DMA transfer */ + if (configure_dma(pl022)) { + dev_dbg(&pl022->adev->dev, + "configuration of DMA failed, fall back to interrupt mode\n"); + goto err_config_dma; + } + /* Disable interrupts in DMA mode, IRQ from DMA controller */ + irqflags = DISABLE_ALL_INTERRUPTS; + } +err_config_dma: /* Enable SSP, turn on interrupts */ writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE), SSP_CR1(pl022->virtbase)); - writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase)); + writew(irqflags, SSP_IMSC(pl022->virtbase)); } -static void do_polling_transfer(void *data) +static void do_polling_transfer(struct pl022 *pl022) { - struct pl022 *pl022 = data; struct spi_message *message = NULL; struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; @@ -1101,7 +1448,7 @@ static void do_polling_transfer(void *data) * * This function checks if there is any spi message in the queue that * needs processing and delegate control to appropriate function - * do_polling_transfer()/do_interrupt_transfer()/do_dma_transfer() + * do_polling_transfer()/do_interrupt_dma_transfer() * based on the kind of the transfer * */ @@ -1150,10 +1497,8 @@ static void pump_messages(struct work_struct *work) if (pl022->cur_chip->xfer_type == POLLING_TRANSFER) do_polling_transfer(pl022); - else if (pl022->cur_chip->xfer_type == INTERRUPT_TRANSFER) - do_interrupt_transfer(pl022); else - do_dma_transfer(pl022); + do_interrupt_dma_transfer(pl022); } @@ -1468,23 +1813,6 @@ static int calculate_effective_freq(struct pl022 *pl022, return 0; } -/** - * NOT IMPLEMENTED - * process_dma_info - Processes the DMA info provided by client drivers - * @chip_info: chip info provided by client device - * @chip: Runtime state maintained by the SSP controller for each spi device - * - * This function processes and stores DMA config provided by client driver - * into the runtime state maintained by the SSP controller driver - */ -static int process_dma_info(struct pl022_config_chip *chip_info, - struct chip_data *chip) -{ - dev_err(chip_info->dev, - "cannot process DMA info, DMA not implemented!\n"); - return -ENOTSUPP; -} - /** * pl022_setup - setup function registered to SPI master framework * @spi: spi device which is requesting setup @@ -1552,8 +1880,6 @@ static int pl022_setup(struct spi_device *spi) dev_dbg(&spi->dev, "allocated memory for controller data\n"); - /* Pointer back to the SPI device */ - chip_info->dev = &spi->dev; /* * Set controller data default values: * Polling is supported by default @@ -1579,6 +1905,9 @@ static int pl022_setup(struct spi_device *spi) "using user supplied controller_data settings\n"); } + /* Pointer back to the SPI device */ + chip_info->dev = &spi->dev; + /* * We can override with custom divisors, else we use the board * frequency setting @@ -1637,9 +1966,8 @@ static int pl022_setup(struct spi_device *spi) chip->cpsr = 0; if ((chip_info->com_mode == DMA_TRANSFER) && ((pl022->master_info)->enable_dma)) { - chip->enable_dma = 1; + chip->enable_dma = true; dev_dbg(&spi->dev, "DMA mode set in controller state\n"); - status = process_dma_info(chip_info, chip); if (status < 0) goto err_config_params; SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED, @@ -1647,7 +1975,7 @@ static int pl022_setup(struct spi_device *spi) SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED, SSP_DMACR_MASK_TXDMAE, 1); } else { - chip->enable_dma = 0; + chip->enable_dma = false; dev_dbg(&spi->dev, "DMA mode NOT set in controller state\n"); SSP_WRITE_BITS(chip->dmacr, SSP_DMA_DISABLED, SSP_DMACR_MASK_RXDMAE, 0); @@ -1773,6 +2101,7 @@ pl022_probe(struct amba_device *adev, struct amba_id *id) if (status) goto err_no_ioregion; + pl022->phybase = adev->res.start; pl022->virtbase = ioremap(adev->res.start, resource_size(&adev->res)); if (pl022->virtbase == NULL) { status = -ENOMEM; @@ -1799,6 +2128,14 @@ pl022_probe(struct amba_device *adev, struct amba_id *id) dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status); goto err_no_irq; } + + /* Get DMA channels */ + if (platform_info->enable_dma) { + status = pl022_dma_probe(pl022); + if (status != 0) + goto err_no_dma; + } + /* Initialize and start queue */ status = init_queue(pl022); if (status != 0) { @@ -1827,6 +2164,8 @@ pl022_probe(struct amba_device *adev, struct amba_id *id) err_start_queue: err_init_queue: destroy_queue(pl022); + pl022_dma_remove(pl022); + err_no_dma: free_irq(adev->irq[0], pl022); err_no_irq: clk_put(pl022->clk); @@ -1857,6 +2196,7 @@ pl022_remove(struct amba_device *adev) return status; } load_ssp_default_config(pl022); + pl022_dma_remove(pl022); free_irq(adev->irq[0], pl022); clk_disable(pl022->clk); clk_put(pl022->clk); -- cgit v1.2.2 From fadcf49b9bd7ec5fb69befbf477e747d5b6a0328 Mon Sep 17 00:00:00 2001 From: matt mooney Date: Fri, 24 Sep 2010 12:17:32 -0700 Subject: spi: change to new flag variable Replace EXTRA_CFLAGS with ccflags-y. Signed-off-by: matt mooney Signed-off-by: Grant Likely --- drivers/spi/Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 65a232d50bc8..f32d11e1ffec 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -2,9 +2,7 @@ # Makefile for kernel SPI drivers. # -ifeq ($(CONFIG_SPI_DEBUG),y) -EXTRA_CFLAGS += -DDEBUG -endif +ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG # small core, mostly translating board-specific # config declarations into driver model code -- cgit v1.2.2 From bde435a9ca376d0b7809768ca803dbf14416b9c1 Mon Sep 17 00:00:00 2001 From: Kevin Wells Date: Thu, 16 Sep 2010 06:18:50 -0700 Subject: spi/pl022: Add spi->mode support to AMBA SPI driver This patch adds spi->mode support for the AMBA pl022 driver and allows spidev to correctly alter SPI modes. Unused fields used in the pl022 header file for the pl022_config_chip have been removed. The ab8500 client driver selects the data transfer size instead of the platform data. For platforms that use the amba pl022 driver, the unused fields in the controller data structure have been removed and the .mode field in the SPI board info structure is used instead. Signed-off-by: Kevin Wells Tested-by: Linus Walleij Acked-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 121 +++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 68 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index db0c67908d2b..59c90f3ccc26 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1595,12 +1595,6 @@ static int destroy_queue(struct pl022 *pl022) static int verify_controller_parameters(struct pl022 *pl022, struct pl022_config_chip *chip_info) { - if ((chip_info->lbm != LOOPBACK_ENABLED) - && (chip_info->lbm != LOOPBACK_DISABLED)) { - dev_err(chip_info->dev, - "loopback Mode is configured incorrectly\n"); - return -EINVAL; - } if ((chip_info->iface < SSP_INTERFACE_MOTOROLA_SPI) || (chip_info->iface > SSP_INTERFACE_UNIDIRECTIONAL)) { dev_err(chip_info->dev, @@ -1626,24 +1620,6 @@ static int verify_controller_parameters(struct pl022 *pl022, "cpsdvsr is configured incorrectly\n"); return -EINVAL; } - if ((chip_info->endian_rx != SSP_RX_MSB) - && (chip_info->endian_rx != SSP_RX_LSB)) { - dev_err(chip_info->dev, - "RX FIFO endianess is configured incorrectly\n"); - return -EINVAL; - } - if ((chip_info->endian_tx != SSP_TX_MSB) - && (chip_info->endian_tx != SSP_TX_LSB)) { - dev_err(chip_info->dev, - "TX FIFO endianess is configured incorrectly\n"); - return -EINVAL; - } - if ((chip_info->data_size < SSP_DATA_BITS_4) - || (chip_info->data_size > SSP_DATA_BITS_32)) { - dev_err(chip_info->dev, - "DATA Size is configured incorrectly\n"); - return -EINVAL; - } if ((chip_info->com_mode != INTERRUPT_TRANSFER) && (chip_info->com_mode != DMA_TRANSFER) && (chip_info->com_mode != POLLING_TRANSFER)) { @@ -1663,20 +1639,6 @@ static int verify_controller_parameters(struct pl022 *pl022, "TX FIFO Trigger Level is configured incorrectly\n"); return -EINVAL; } - if (chip_info->iface == SSP_INTERFACE_MOTOROLA_SPI) { - if ((chip_info->clk_phase != SSP_CLK_FIRST_EDGE) - && (chip_info->clk_phase != SSP_CLK_SECOND_EDGE)) { - dev_err(chip_info->dev, - "Clock Phase is configured incorrectly\n"); - return -EINVAL; - } - if ((chip_info->clk_pol != SSP_CLK_POL_IDLE_LOW) - && (chip_info->clk_pol != SSP_CLK_POL_IDLE_HIGH)) { - dev_err(chip_info->dev, - "Clock Polarity is configured incorrectly\n"); - return -EINVAL; - } - } if (chip_info->iface == SSP_INTERFACE_NATIONAL_MICROWIRE) { if ((chip_info->ctrl_len < SSP_BITS_4) || (chip_info->ctrl_len > SSP_BITS_32)) { @@ -1825,23 +1787,14 @@ static int calculate_effective_freq(struct pl022 *pl022, * controller hardware here, that is not done until the actual transfer * commence. */ - -/* FIXME: JUST GUESSING the spi->mode bits understood by this driver */ -#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \ - | SPI_LSB_FIRST | SPI_LOOP) - static int pl022_setup(struct spi_device *spi) { struct pl022_config_chip *chip_info; struct chip_data *chip; int status = 0; struct pl022 *pl022 = spi_master_get_devdata(spi->master); - - if (spi->mode & ~MODEBITS) { - dev_dbg(&spi->dev, "unsupported mode bits %x\n", - spi->mode & ~MODEBITS); - return -EINVAL; - } + unsigned int bits = spi->bits_per_word; + u32 tmp; if (!spi->max_speed_hz) return -EINVAL; @@ -1884,18 +1837,12 @@ static int pl022_setup(struct spi_device *spi) * Set controller data default values: * Polling is supported by default */ - chip_info->lbm = LOOPBACK_DISABLED; chip_info->com_mode = POLLING_TRANSFER; chip_info->iface = SSP_INTERFACE_MOTOROLA_SPI; chip_info->hierarchy = SSP_SLAVE; chip_info->slave_tx_disable = DO_NOT_DRIVE_TX; - chip_info->endian_tx = SSP_TX_LSB; - chip_info->endian_rx = SSP_RX_LSB; - chip_info->data_size = SSP_DATA_BITS_12; chip_info->rx_lev_trig = SSP_RX_1_OR_MORE_ELEM; chip_info->tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC; - chip_info->clk_phase = SSP_CLK_SECOND_EDGE; - chip_info->clk_pol = SSP_CLK_POL_IDLE_LOW; chip_info->ctrl_len = SSP_BITS_8; chip_info->wait_state = SSP_MWIRE_WAIT_ZERO; chip_info->duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX; @@ -1933,12 +1880,16 @@ static int pl022_setup(struct spi_device *spi) chip->xfer_type = chip_info->com_mode; chip->cs_control = chip_info->cs_control; - if (chip_info->data_size <= 8) { - dev_dbg(&spi->dev, "1 <= n <=8 bits per word\n"); + if (bits <= 3) { + /* PL022 doesn't support less than 4-bits */ + status = -ENOTSUPP; + goto err_config_params; + } else if (bits <= 8) { + dev_dbg(&spi->dev, "4 <= n <=8 bits per word\n"); chip->n_bytes = 1; chip->read = READING_U8; chip->write = WRITING_U8; - } else if (chip_info->data_size <= 16) { + } else if (bits <= 16) { dev_dbg(&spi->dev, "9 <= n <= 16 bits per word\n"); chip->n_bytes = 2; chip->read = READING_U16; @@ -1955,6 +1906,7 @@ static int pl022_setup(struct spi_device *spi) dev_err(&spi->dev, "a standard pl022 can only handle " "1 <= n <= 16 bit words\n"); + status = -ENOTSUPP; goto err_config_params; } } @@ -1987,6 +1939,8 @@ static int pl022_setup(struct spi_device *spi) /* Special setup for the ST micro extended control registers */ if (pl022->vendor->extended_cr) { + u32 etx; + if (pl022->vendor->pl023) { /* These bits are only in the PL023 */ SSP_WRITE_BITS(chip->cr1, chip_info->clkdelay, @@ -2002,29 +1956,51 @@ static int pl022_setup(struct spi_device *spi) SSP_WRITE_BITS(chip->cr1, chip_info->wait_state, SSP_CR1_MASK_MWAIT_ST, 6); } - SSP_WRITE_BITS(chip->cr0, chip_info->data_size, + SSP_WRITE_BITS(chip->cr0, bits - 1, SSP_CR0_MASK_DSS_ST, 0); - SSP_WRITE_BITS(chip->cr1, chip_info->endian_rx, - SSP_CR1_MASK_RENDN_ST, 4); - SSP_WRITE_BITS(chip->cr1, chip_info->endian_tx, - SSP_CR1_MASK_TENDN_ST, 5); + + if (spi->mode & SPI_LSB_FIRST) { + tmp = SSP_RX_LSB; + etx = SSP_TX_LSB; + } else { + tmp = SSP_RX_MSB; + etx = SSP_TX_MSB; + } + SSP_WRITE_BITS(chip->cr1, tmp, SSP_CR1_MASK_RENDN_ST, 4); + SSP_WRITE_BITS(chip->cr1, etx, SSP_CR1_MASK_TENDN_ST, 5); SSP_WRITE_BITS(chip->cr1, chip_info->rx_lev_trig, SSP_CR1_MASK_RXIFLSEL_ST, 7); SSP_WRITE_BITS(chip->cr1, chip_info->tx_lev_trig, SSP_CR1_MASK_TXIFLSEL_ST, 10); } else { - SSP_WRITE_BITS(chip->cr0, chip_info->data_size, + SSP_WRITE_BITS(chip->cr0, bits - 1, SSP_CR0_MASK_DSS, 0); SSP_WRITE_BITS(chip->cr0, chip_info->iface, SSP_CR0_MASK_FRF, 4); } + /* Stuff that is common for all versions */ - SSP_WRITE_BITS(chip->cr0, chip_info->clk_pol, SSP_CR0_MASK_SPO, 6); - SSP_WRITE_BITS(chip->cr0, chip_info->clk_phase, SSP_CR0_MASK_SPH, 7); + if (spi->mode & SPI_CPOL) + tmp = SSP_CLK_POL_IDLE_HIGH; + else + tmp = SSP_CLK_POL_IDLE_LOW; + SSP_WRITE_BITS(chip->cr0, tmp, SSP_CR0_MASK_SPO, 6); + + if (spi->mode & SPI_CPHA) + tmp = SSP_CLK_SECOND_EDGE; + else + tmp = SSP_CLK_FIRST_EDGE; + SSP_WRITE_BITS(chip->cr0, tmp, SSP_CR0_MASK_SPH, 7); + SSP_WRITE_BITS(chip->cr0, chip_info->clk_freq.scr, SSP_CR0_MASK_SCR, 8); /* Loopback is available on all versions except PL023 */ - if (!pl022->vendor->pl023) - SSP_WRITE_BITS(chip->cr1, chip_info->lbm, SSP_CR1_MASK_LBM, 0); + if (!pl022->vendor->pl023) { + if (spi->mode & SPI_LOOP) + tmp = LOOPBACK_ENABLED; + else + tmp = LOOPBACK_DISABLED; + SSP_WRITE_BITS(chip->cr1, tmp, SSP_CR1_MASK_LBM, 0); + } SSP_WRITE_BITS(chip->cr1, SSP_DISABLED, SSP_CR1_MASK_SSE, 1); SSP_WRITE_BITS(chip->cr1, chip_info->hierarchy, SSP_CR1_MASK_MS, 2); SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD, 3); @@ -2033,6 +2009,7 @@ static int pl022_setup(struct spi_device *spi) spi_set_ctldata(spi, chip); return status; err_config_params: + spi_set_ctldata(spi, NULL); err_first_setup: kfree(chip); return status; @@ -2095,6 +2072,14 @@ pl022_probe(struct amba_device *adev, struct amba_id *id) master->setup = pl022_setup; master->transfer = pl022_transfer; + /* + * Supports mode 0-3, loopback, and active low CS. Transfers are + * always MS bit first on the original pl022. + */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; + if (pl022->vendor->extended_cr) + master->mode_bits |= SPI_LSB_FIRST; + dev_dbg(&adev->dev, "BUSNO: %d\n", master->bus_num); status = amba_request_regions(adev, NULL); -- cgit v1.2.2 From 5a1c98be1de165c8ad1bd5343a5d779230669489 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 1 Oct 2010 11:47:32 +0200 Subject: spi/pl022: get rid of chipinfo dev pointer What is the dev pointer doing inside the platform data anyway. We have another pointer to the actual device at hand, use that. Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 59c90f3ccc26..19d54aa31a36 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1597,58 +1597,58 @@ static int verify_controller_parameters(struct pl022 *pl022, { if ((chip_info->iface < SSP_INTERFACE_MOTOROLA_SPI) || (chip_info->iface > SSP_INTERFACE_UNIDIRECTIONAL)) { - dev_err(chip_info->dev, + dev_err(&pl022->adev->dev, "interface is configured incorrectly\n"); return -EINVAL; } if ((chip_info->iface == SSP_INTERFACE_UNIDIRECTIONAL) && (!pl022->vendor->unidir)) { - dev_err(chip_info->dev, + dev_err(&pl022->adev->dev, "unidirectional mode not supported in this " "hardware version\n"); return -EINVAL; } if ((chip_info->hierarchy != SSP_MASTER) && (chip_info->hierarchy != SSP_SLAVE)) { - dev_err(chip_info->dev, + dev_err(&pl022->adev->dev, "hierarchy is configured incorrectly\n"); return -EINVAL; } if (((chip_info->clk_freq).cpsdvsr < CPSDVR_MIN) || ((chip_info->clk_freq).cpsdvsr > CPSDVR_MAX)) { - dev_err(chip_info->dev, + dev_err(&pl022->adev->dev, "cpsdvsr is configured incorrectly\n"); return -EINVAL; } if ((chip_info->com_mode != INTERRUPT_TRANSFER) && (chip_info->com_mode != DMA_TRANSFER) && (chip_info->com_mode != POLLING_TRANSFER)) { - dev_err(chip_info->dev, + dev_err(&pl022->adev->dev, "Communication mode is configured incorrectly\n"); return -EINVAL; } if ((chip_info->rx_lev_trig < SSP_RX_1_OR_MORE_ELEM) || (chip_info->rx_lev_trig > SSP_RX_32_OR_MORE_ELEM)) { - dev_err(chip_info->dev, + dev_err(&pl022->adev->dev, "RX FIFO Trigger Level is configured incorrectly\n"); return -EINVAL; } if ((chip_info->tx_lev_trig < SSP_TX_1_OR_MORE_EMPTY_LOC) || (chip_info->tx_lev_trig > SSP_TX_32_OR_MORE_EMPTY_LOC)) { - dev_err(chip_info->dev, + dev_err(&pl022->adev->dev, "TX FIFO Trigger Level is configured incorrectly\n"); return -EINVAL; } if (chip_info->iface == SSP_INTERFACE_NATIONAL_MICROWIRE) { if ((chip_info->ctrl_len < SSP_BITS_4) || (chip_info->ctrl_len > SSP_BITS_32)) { - dev_err(chip_info->dev, + dev_err(&pl022->adev->dev, "CTRL LEN is configured incorrectly\n"); return -EINVAL; } if ((chip_info->wait_state != SSP_MWIRE_WAIT_ZERO) && (chip_info->wait_state != SSP_MWIRE_WAIT_ONE)) { - dev_err(chip_info->dev, + dev_err(&pl022->adev->dev, "Wait State is configured incorrectly\n"); return -EINVAL; } @@ -1658,13 +1658,13 @@ static int verify_controller_parameters(struct pl022 *pl022, SSP_MICROWIRE_CHANNEL_FULL_DUPLEX) && (chip_info->duplex != SSP_MICROWIRE_CHANNEL_HALF_DUPLEX)) { - dev_err(chip_info->dev, + dev_err(&pl022->adev->dev, "Microwire duplex mode is configured incorrectly\n"); return -EINVAL; } } else { if (chip_info->duplex != SSP_MICROWIRE_CHANNEL_FULL_DUPLEX) - dev_err(chip_info->dev, + dev_err(&pl022->adev->dev, "Microwire half duplex mode requested," " but this is only available in the" " ST version of PL022\n"); @@ -1672,7 +1672,7 @@ static int verify_controller_parameters(struct pl022 *pl022, } } if (chip_info->cs_control == NULL) { - dev_warn(chip_info->dev, + dev_warn(&pl022->adev->dev, "Chip Select Function is NULL for this chip\n"); chip_info->cs_control = null_cs_control; } @@ -1852,9 +1852,6 @@ static int pl022_setup(struct spi_device *spi) "using user supplied controller_data settings\n"); } - /* Pointer back to the SPI device */ - chip_info->dev = &spi->dev; - /* * We can override with custom divisors, else we use the board * frequency setting -- cgit v1.2.2 From f9d629c737cb6687216a0c540b5466a4bd8b070a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 1 Oct 2010 13:33:13 +0200 Subject: spi/pl022: fix dubious allocation staticize platform data This removes some dubious allocation of a local chipinfo struct in favor of a constant preset, tagging that one const revealed further problems with platform data being modified so fixed up these too. Reported-by: Virupax Sadashivpetimath Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 96 +++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 50 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 19d54aa31a36..fb3d1b31772d 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1593,7 +1593,7 @@ static int destroy_queue(struct pl022 *pl022) } static int verify_controller_parameters(struct pl022 *pl022, - struct pl022_config_chip *chip_info) + struct pl022_config_chip const *chip_info) { if ((chip_info->iface < SSP_INTERFACE_MOTOROLA_SPI) || (chip_info->iface > SSP_INTERFACE_UNIDIRECTIONAL)) { @@ -1614,12 +1614,6 @@ static int verify_controller_parameters(struct pl022 *pl022, "hierarchy is configured incorrectly\n"); return -EINVAL; } - if (((chip_info->clk_freq).cpsdvsr < CPSDVR_MIN) - || ((chip_info->clk_freq).cpsdvsr > CPSDVR_MAX)) { - dev_err(&pl022->adev->dev, - "cpsdvsr is configured incorrectly\n"); - return -EINVAL; - } if ((chip_info->com_mode != INTERRUPT_TRANSFER) && (chip_info->com_mode != DMA_TRANSFER) && (chip_info->com_mode != POLLING_TRANSFER)) { @@ -1671,11 +1665,6 @@ static int verify_controller_parameters(struct pl022 *pl022, return -EINVAL; } } - if (chip_info->cs_control == NULL) { - dev_warn(&pl022->adev->dev, - "Chip Select Function is NULL for this chip\n"); - chip_info->cs_control = null_cs_control; - } return 0; } @@ -1775,6 +1764,25 @@ static int calculate_effective_freq(struct pl022 *pl022, return 0; } + +/* + * A piece of default chip info unless the platform + * supplies it. + */ +static const struct pl022_config_chip pl022_default_chip_info = { + .com_mode = POLLING_TRANSFER, + .iface = SSP_INTERFACE_MOTOROLA_SPI, + .hierarchy = SSP_SLAVE, + .slave_tx_disable = DO_NOT_DRIVE_TX, + .rx_lev_trig = SSP_RX_1_OR_MORE_ELEM, + .tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC, + .ctrl_len = SSP_BITS_8, + .wait_state = SSP_MWIRE_WAIT_ZERO, + .duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX, + .cs_control = null_cs_control, +}; + + /** * pl022_setup - setup function registered to SPI master framework * @spi: spi device which is requesting setup @@ -1789,8 +1797,9 @@ static int calculate_effective_freq(struct pl022 *pl022, */ static int pl022_setup(struct spi_device *spi) { - struct pl022_config_chip *chip_info; + struct pl022_config_chip const *chip_info; struct chip_data *chip; + struct ssp_clock_params clk_freq; int status = 0; struct pl022 *pl022 = spi_master_get_devdata(spi->master); unsigned int bits = spi->bits_per_word; @@ -1817,40 +1826,13 @@ static int pl022_setup(struct spi_device *spi) chip_info = spi->controller_data; if (chip_info == NULL) { + chip_info = &pl022_default_chip_info; /* spi_board_info.controller_data not is supplied */ dev_dbg(&spi->dev, "using default controller_data settings\n"); - - chip_info = - kzalloc(sizeof(struct pl022_config_chip), GFP_KERNEL); - - if (!chip_info) { - dev_err(&spi->dev, - "cannot allocate controller data\n"); - status = -ENOMEM; - goto err_first_setup; - } - - dev_dbg(&spi->dev, "allocated memory for controller data\n"); - - /* - * Set controller data default values: - * Polling is supported by default - */ - chip_info->com_mode = POLLING_TRANSFER; - chip_info->iface = SSP_INTERFACE_MOTOROLA_SPI; - chip_info->hierarchy = SSP_SLAVE; - chip_info->slave_tx_disable = DO_NOT_DRIVE_TX; - chip_info->rx_lev_trig = SSP_RX_1_OR_MORE_ELEM; - chip_info->tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC; - chip_info->ctrl_len = SSP_BITS_8; - chip_info->wait_state = SSP_MWIRE_WAIT_ZERO; - chip_info->duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX; - chip_info->cs_control = null_cs_control; - } else { + } else dev_dbg(&spi->dev, "using user supplied controller_data settings\n"); - } /* * We can override with custom divisors, else we use the board @@ -1860,22 +1842,37 @@ static int pl022_setup(struct spi_device *spi) && (0 == chip_info->clk_freq.scr)) { status = calculate_effective_freq(pl022, spi->max_speed_hz, - &chip_info->clk_freq); + &clk_freq); if (status < 0) goto err_config_params; } else { - if ((chip_info->clk_freq.cpsdvsr % 2) != 0) - chip_info->clk_freq.cpsdvsr = - chip_info->clk_freq.cpsdvsr - 1; + memcpy(&clk_freq, &chip_info->clk_freq, sizeof(clk_freq)); + if ((clk_freq.cpsdvsr % 2) != 0) + clk_freq.cpsdvsr = + clk_freq.cpsdvsr - 1; } + if ((clk_freq.cpsdvsr < CPSDVR_MIN) + || (clk_freq.cpsdvsr > CPSDVR_MAX)) { + dev_err(&spi->dev, + "cpsdvsr is configured incorrectly\n"); + goto err_config_params; + } + + status = verify_controller_parameters(pl022, chip_info); if (status) { dev_err(&spi->dev, "controller data is incorrect"); goto err_config_params; } + /* Now set controller state based on controller data */ chip->xfer_type = chip_info->com_mode; - chip->cs_control = chip_info->cs_control; + if (!chip_info->cs_control) { + chip->cs_control = null_cs_control; + dev_warn(&spi->dev, + "chip select function is NULL for this chip\n"); + } else + chip->cs_control = chip_info->cs_control; if (bits <= 3) { /* PL022 doesn't support less than 4-bits */ @@ -1932,7 +1929,7 @@ static int pl022_setup(struct spi_device *spi) SSP_DMACR_MASK_TXDMAE, 1); } - chip->cpsr = chip_info->clk_freq.cpsdvsr; + chip->cpsr = clk_freq.cpsdvsr; /* Special setup for the ST micro extended control registers */ if (pl022->vendor->extended_cr) { @@ -1989,7 +1986,7 @@ static int pl022_setup(struct spi_device *spi) tmp = SSP_CLK_FIRST_EDGE; SSP_WRITE_BITS(chip->cr0, tmp, SSP_CR0_MASK_SPH, 7); - SSP_WRITE_BITS(chip->cr0, chip_info->clk_freq.scr, SSP_CR0_MASK_SCR, 8); + SSP_WRITE_BITS(chip->cr0, clk_freq.scr, SSP_CR0_MASK_SCR, 8); /* Loopback is available on all versions except PL023 */ if (!pl022->vendor->pl023) { if (spi->mode & SPI_LOOP) @@ -2007,7 +2004,6 @@ static int pl022_setup(struct spi_device *spi) return status; err_config_params: spi_set_ctldata(spi, NULL); - err_first_setup: kfree(chip); return status; } -- cgit v1.2.2 From 3272029fb33a88873b9b02224ebeb23bf3a4668e Mon Sep 17 00:00:00 2001 From: Mingkai Hu Date: Tue, 12 Oct 2010 18:18:30 +0800 Subject: spi/mpc8xxx: rename spi_mpc8xxx.c to spi_fsl_spi.c This will pave the way to refactor out the common code which can be used by the eSPI controller driver, and rename the SPI controller dirver to the file spi_fsl_spi.c. Signed-off-by: Mingkai Hu Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 9 +- drivers/spi/Makefile | 2 +- drivers/spi/spi_fsl_spi.c | 1425 +++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/spi_mpc8xxx.c | 1425 --------------------------------------------- 4 files changed, 1431 insertions(+), 1430 deletions(-) create mode 100644 drivers/spi/spi_fsl_spi.c delete mode 100644 drivers/spi/spi_mpc8xxx.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index d966de1afc52..e8f02a6f2d23 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -182,12 +182,13 @@ config SPI_MPC512x_PSC This enables using the Freescale MPC5121 Programmable Serial Controller in SPI master mode. -config SPI_MPC8xxx - tristate "Freescale MPC8xxx SPI controller" +config SPI_FSL_SPI + tristate "Freescale SPI controller" depends on FSL_SOC help - This enables using the Freescale MPC8xxx SPI controllers in master - mode. + This enables using the Freescale SPI controllers in master mode. + MPC83xx platform uses the controller in cpu mode or CPM/QE mode. + MPC8569 uses the controller in QE mode, MPC8610 in cpu mode. config SPI_OMAP_UWIRE tristate "OMAP1 MicroWire" diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index f32d11e1ffec..c6a56c798823 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -32,7 +32,7 @@ obj-$(CONFIG_SPI_PL022) += amba-pl022.o obj-$(CONFIG_SPI_MPC512x_PSC) += mpc512x_psc_spi.o obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o -obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o +obj-$(CONFIG_SPI_FSL_SPI) += spi_fsl_spi.o obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o diff --git a/drivers/spi/spi_fsl_spi.c b/drivers/spi/spi_fsl_spi.c new file mode 100644 index 000000000000..1dd86b835cd8 --- /dev/null +++ b/drivers/spi/spi_fsl_spi.c @@ -0,0 +1,1425 @@ +/* + * MPC8xxx SPI controller driver. + * + * Maintainer: Kumar Gala + * + * Copyright (C) 2006 Polycom, Inc. + * + * CPM SPI and QE buffer descriptors mode support: + * Copyright (c) 2009 MontaVista Software, Inc. + * Author: Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* CPM1 and CPM2 are mutually exclusive. */ +#ifdef CONFIG_CPM1 +#include +#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0) +#else +#include +#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0) +#endif + +/* SPI Controller registers */ +struct mpc8xxx_spi_reg { + u8 res1[0x20]; + __be32 mode; + __be32 event; + __be32 mask; + __be32 command; + __be32 transmit; + __be32 receive; +}; + +/* SPI Controller mode register definitions */ +#define SPMODE_LOOP (1 << 30) +#define SPMODE_CI_INACTIVEHIGH (1 << 29) +#define SPMODE_CP_BEGIN_EDGECLK (1 << 28) +#define SPMODE_DIV16 (1 << 27) +#define SPMODE_REV (1 << 26) +#define SPMODE_MS (1 << 25) +#define SPMODE_ENABLE (1 << 24) +#define SPMODE_LEN(x) ((x) << 20) +#define SPMODE_PM(x) ((x) << 16) +#define SPMODE_OP (1 << 14) +#define SPMODE_CG(x) ((x) << 7) + +/* + * Default for SPI Mode: + * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk + */ +#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \ + SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf)) + +/* SPIE register values */ +#define SPIE_NE 0x00000200 /* Not empty */ +#define SPIE_NF 0x00000100 /* Not full */ + +/* SPIM register values */ +#define SPIM_NE 0x00000200 /* Not empty */ +#define SPIM_NF 0x00000100 /* Not full */ + +#define SPIE_TXB 0x00000200 /* Last char is written to tx fifo */ +#define SPIE_RXB 0x00000100 /* Last char is written to rx buf */ + +/* SPCOM register values */ +#define SPCOM_STR (1 << 23) /* Start transmit */ + +#define SPI_PRAM_SIZE 0x100 +#define SPI_MRBLR ((unsigned int)PAGE_SIZE) + +/* SPI Controller driver's private data. */ +struct mpc8xxx_spi { + struct device *dev; + struct mpc8xxx_spi_reg __iomem *base; + + /* rx & tx bufs from the spi_transfer */ + const void *tx; + void *rx; + + int subblock; + struct spi_pram __iomem *pram; + struct cpm_buf_desc __iomem *tx_bd; + struct cpm_buf_desc __iomem *rx_bd; + + struct spi_transfer *xfer_in_progress; + + /* dma addresses for CPM transfers */ + dma_addr_t tx_dma; + dma_addr_t rx_dma; + bool map_tx_dma; + bool map_rx_dma; + + dma_addr_t dma_dummy_tx; + dma_addr_t dma_dummy_rx; + + /* functions to deal with different sized buffers */ + void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *); + u32(*get_tx) (struct mpc8xxx_spi *); + + unsigned int count; + unsigned int irq; + + unsigned nsecs; /* (clock cycle time)/2 */ + + u32 spibrg; /* SPIBRG input clock */ + u32 rx_shift; /* RX data reg shift when in qe mode */ + u32 tx_shift; /* TX data reg shift when in qe mode */ + + unsigned int flags; + + struct workqueue_struct *workqueue; + struct work_struct work; + + struct list_head queue; + spinlock_t lock; + + struct completion done; +}; + +static void *mpc8xxx_dummy_rx; +static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock); +static int mpc8xxx_dummy_rx_refcnt; + +struct spi_mpc8xxx_cs { + /* functions to deal with different sized buffers */ + void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *); + u32 (*get_tx) (struct mpc8xxx_spi *); + u32 rx_shift; /* RX data reg shift when in qe mode */ + u32 tx_shift; /* TX data reg shift when in qe mode */ + u32 hw_mode; /* Holds HW mode register settings */ +}; + +static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val) +{ + out_be32(reg, val); +} + +static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg) +{ + return in_be32(reg); +} + +#define MPC83XX_SPI_RX_BUF(type) \ +static \ +void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \ +{ \ + type *rx = mpc8xxx_spi->rx; \ + *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \ + mpc8xxx_spi->rx = rx; \ +} + +#define MPC83XX_SPI_TX_BUF(type) \ +static \ +u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \ +{ \ + u32 data; \ + const type *tx = mpc8xxx_spi->tx; \ + if (!tx) \ + return 0; \ + data = *tx++ << mpc8xxx_spi->tx_shift; \ + mpc8xxx_spi->tx = tx; \ + return data; \ +} + +MPC83XX_SPI_RX_BUF(u8) +MPC83XX_SPI_RX_BUF(u16) +MPC83XX_SPI_RX_BUF(u32) +MPC83XX_SPI_TX_BUF(u8) +MPC83XX_SPI_TX_BUF(u16) +MPC83XX_SPI_TX_BUF(u32) + +static void mpc8xxx_spi_change_mode(struct spi_device *spi) +{ + struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); + struct spi_mpc8xxx_cs *cs = spi->controller_state; + __be32 __iomem *mode = &mspi->base->mode; + unsigned long flags; + + if (cs->hw_mode == mpc8xxx_spi_read_reg(mode)) + return; + + /* Turn off IRQs locally to minimize time that SPI is disabled. */ + local_irq_save(flags); + + /* Turn off SPI unit prior changing mode */ + mpc8xxx_spi_write_reg(mode, cs->hw_mode & ~SPMODE_ENABLE); + + /* When in CPM mode, we need to reinit tx and rx. */ + if (mspi->flags & SPI_CPM_MODE) { + if (mspi->flags & SPI_QE) { + qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock, + QE_CR_PROTOCOL_UNSPECIFIED, 0); + } else { + cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX); + if (mspi->flags & SPI_CPM1) { + out_be16(&mspi->pram->rbptr, + in_be16(&mspi->pram->rbase)); + out_be16(&mspi->pram->tbptr, + in_be16(&mspi->pram->tbase)); + } + } + } + mpc8xxx_spi_write_reg(mode, cs->hw_mode); + local_irq_restore(flags); +} + +static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value) +{ + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data; + bool pol = spi->mode & SPI_CS_HIGH; + struct spi_mpc8xxx_cs *cs = spi->controller_state; + + if (value == BITBANG_CS_INACTIVE) { + if (pdata->cs_control) + pdata->cs_control(spi, !pol); + } + + if (value == BITBANG_CS_ACTIVE) { + mpc8xxx_spi->rx_shift = cs->rx_shift; + mpc8xxx_spi->tx_shift = cs->tx_shift; + mpc8xxx_spi->get_rx = cs->get_rx; + mpc8xxx_spi->get_tx = cs->get_tx; + + mpc8xxx_spi_change_mode(spi); + + if (pdata->cs_control) + pdata->cs_control(spi, pol); + } +} + +static int +mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs, + struct spi_device *spi, + struct mpc8xxx_spi *mpc8xxx_spi, + int bits_per_word) +{ + cs->rx_shift = 0; + cs->tx_shift = 0; + if (bits_per_word <= 8) { + cs->get_rx = mpc8xxx_spi_rx_buf_u8; + cs->get_tx = mpc8xxx_spi_tx_buf_u8; + if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { + cs->rx_shift = 16; + cs->tx_shift = 24; + } + } else if (bits_per_word <= 16) { + cs->get_rx = mpc8xxx_spi_rx_buf_u16; + cs->get_tx = mpc8xxx_spi_tx_buf_u16; + if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { + cs->rx_shift = 16; + cs->tx_shift = 16; + } + } else if (bits_per_word <= 32) { + cs->get_rx = mpc8xxx_spi_rx_buf_u32; + cs->get_tx = mpc8xxx_spi_tx_buf_u32; + } else + return -EINVAL; + + if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE && + spi->mode & SPI_LSB_FIRST) { + cs->tx_shift = 0; + if (bits_per_word <= 8) + cs->rx_shift = 8; + else + cs->rx_shift = 0; + } + mpc8xxx_spi->rx_shift = cs->rx_shift; + mpc8xxx_spi->tx_shift = cs->tx_shift; + mpc8xxx_spi->get_rx = cs->get_rx; + mpc8xxx_spi->get_tx = cs->get_tx; + + return bits_per_word; +} + +static int +mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs, + struct spi_device *spi, + int bits_per_word) +{ + /* QE uses Little Endian for words > 8 + * so transform all words > 8 into 8 bits + * Unfortnatly that doesn't work for LSB so + * reject these for now */ + /* Note: 32 bits word, LSB works iff + * tfcr/rfcr is set to CPMFCR_GBL */ + if (spi->mode & SPI_LSB_FIRST && + bits_per_word > 8) + return -EINVAL; + if (bits_per_word > 8) + return 8; /* pretend its 8 bits */ + return bits_per_word; +} + +static +int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct mpc8xxx_spi *mpc8xxx_spi; + int bits_per_word; + u8 pm; + u32 hz; + struct spi_mpc8xxx_cs *cs = spi->controller_state; + + mpc8xxx_spi = spi_master_get_devdata(spi->master); + + if (t) { + bits_per_word = t->bits_per_word; + hz = t->speed_hz; + } else { + bits_per_word = 0; + hz = 0; + } + + /* spi_transfer level calls that work per-word */ + if (!bits_per_word) + bits_per_word = spi->bits_per_word; + + /* Make sure its a bit width we support [4..16, 32] */ + if ((bits_per_word < 4) + || ((bits_per_word > 16) && (bits_per_word != 32))) + return -EINVAL; + + if (!hz) + hz = spi->max_speed_hz; + + if (!(mpc8xxx_spi->flags & SPI_CPM_MODE)) + bits_per_word = mspi_apply_cpu_mode_quirks(cs, spi, + mpc8xxx_spi, + bits_per_word); + else if (mpc8xxx_spi->flags & SPI_QE) + bits_per_word = mspi_apply_qe_mode_quirks(cs, spi, + bits_per_word); + + if (bits_per_word < 0) + return bits_per_word; + + if (bits_per_word == 32) + bits_per_word = 0; + else + bits_per_word = bits_per_word - 1; + + /* mask out bits we are going to set */ + cs->hw_mode &= ~(SPMODE_LEN(0xF) | SPMODE_DIV16 + | SPMODE_PM(0xF)); + + cs->hw_mode |= SPMODE_LEN(bits_per_word); + + if ((mpc8xxx_spi->spibrg / hz) > 64) { + cs->hw_mode |= SPMODE_DIV16; + pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1; + + WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. " + "Will use %d Hz instead.\n", dev_name(&spi->dev), + hz, mpc8xxx_spi->spibrg / 1024); + if (pm > 16) + pm = 16; + } else + pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1; + if (pm) + pm--; + + cs->hw_mode |= SPMODE_PM(pm); + + mpc8xxx_spi_change_mode(spi); + return 0; +} + +static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi) +{ + struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd; + struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd; + unsigned int xfer_len = min(mspi->count, SPI_MRBLR); + unsigned int xfer_ofs; + + xfer_ofs = mspi->xfer_in_progress->len - mspi->count; + + if (mspi->rx_dma == mspi->dma_dummy_rx) + out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma); + else + out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs); + out_be16(&rx_bd->cbd_datlen, 0); + out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP); + + if (mspi->tx_dma == mspi->dma_dummy_tx) + out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma); + else + out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs); + out_be16(&tx_bd->cbd_datlen, xfer_len); + out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP | + BD_SC_LAST); + + /* start transfer */ + mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR); +} + +static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi, + struct spi_transfer *t, bool is_dma_mapped) +{ + struct device *dev = mspi->dev; + + if (is_dma_mapped) { + mspi->map_tx_dma = 0; + mspi->map_rx_dma = 0; + } else { + mspi->map_tx_dma = 1; + mspi->map_rx_dma = 1; + } + + if (!t->tx_buf) { + mspi->tx_dma = mspi->dma_dummy_tx; + mspi->map_tx_dma = 0; + } + + if (!t->rx_buf) { + mspi->rx_dma = mspi->dma_dummy_rx; + mspi->map_rx_dma = 0; + } + + if (mspi->map_tx_dma) { + void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */ + + mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, mspi->tx_dma)) { + dev_err(dev, "unable to map tx dma\n"); + return -ENOMEM; + } + } else if (t->tx_buf) { + mspi->tx_dma = t->tx_dma; + } + + if (mspi->map_rx_dma) { + mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(dev, mspi->rx_dma)) { + dev_err(dev, "unable to map rx dma\n"); + goto err_rx_dma; + } + } else if (t->rx_buf) { + mspi->rx_dma = t->rx_dma; + } + + /* enable rx ints */ + mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB); + + mspi->xfer_in_progress = t; + mspi->count = t->len; + + /* start CPM transfers */ + mpc8xxx_spi_cpm_bufs_start(mspi); + + return 0; + +err_rx_dma: + if (mspi->map_tx_dma) + dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE); + return -ENOMEM; +} + +static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) +{ + struct device *dev = mspi->dev; + struct spi_transfer *t = mspi->xfer_in_progress; + + if (mspi->map_tx_dma) + dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE); + if (mspi->map_rx_dma) + dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE); + mspi->xfer_in_progress = NULL; +} + +static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi, + struct spi_transfer *t, unsigned int len) +{ + u32 word; + + mspi->count = len; + + /* enable rx ints */ + mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE); + + /* transmit word */ + word = mspi->get_tx(mspi); + mpc8xxx_spi_write_reg(&mspi->base->transmit, word); + + return 0; +} + +static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t, + bool is_dma_mapped) +{ + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + unsigned int len = t->len; + u8 bits_per_word; + int ret; + + bits_per_word = spi->bits_per_word; + if (t->bits_per_word) + bits_per_word = t->bits_per_word; + + if (bits_per_word > 8) { + /* invalid length? */ + if (len & 1) + return -EINVAL; + len /= 2; + } + if (bits_per_word > 16) { + /* invalid length? */ + if (len & 1) + return -EINVAL; + len /= 2; + } + + mpc8xxx_spi->tx = t->tx_buf; + mpc8xxx_spi->rx = t->rx_buf; + + INIT_COMPLETION(mpc8xxx_spi->done); + + if (mpc8xxx_spi->flags & SPI_CPM_MODE) + ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped); + else + ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len); + if (ret) + return ret; + + wait_for_completion(&mpc8xxx_spi->done); + + /* disable rx ints */ + mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0); + + if (mpc8xxx_spi->flags & SPI_CPM_MODE) + mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi); + + return mpc8xxx_spi->count; +} + +static void mpc8xxx_spi_do_one_msg(struct spi_message *m) +{ + struct spi_device *spi = m->spi; + struct spi_transfer *t; + unsigned int cs_change; + const int nsecs = 50; + int status; + + cs_change = 1; + status = 0; + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->bits_per_word || t->speed_hz) { + /* Don't allow changes if CS is active */ + status = -EINVAL; + + if (cs_change) + status = mpc8xxx_spi_setup_transfer(spi, t); + if (status < 0) + break; + } + + if (cs_change) { + mpc8xxx_spi_chipselect(spi, BITBANG_CS_ACTIVE); + ndelay(nsecs); + } + cs_change = t->cs_change; + if (t->len) + status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped); + if (status) { + status = -EMSGSIZE; + break; + } + m->actual_length += t->len; + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (cs_change) { + ndelay(nsecs); + mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE); + ndelay(nsecs); + } + } + + m->status = status; + m->complete(m->context); + + if (status || !cs_change) { + ndelay(nsecs); + mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE); + } + + mpc8xxx_spi_setup_transfer(spi, NULL); +} + +static void mpc8xxx_spi_work(struct work_struct *work) +{ + struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi, + work); + + spin_lock_irq(&mpc8xxx_spi->lock); + while (!list_empty(&mpc8xxx_spi->queue)) { + struct spi_message *m = container_of(mpc8xxx_spi->queue.next, + struct spi_message, queue); + + list_del_init(&m->queue); + spin_unlock_irq(&mpc8xxx_spi->lock); + + mpc8xxx_spi_do_one_msg(m); + + spin_lock_irq(&mpc8xxx_spi->lock); + } + spin_unlock_irq(&mpc8xxx_spi->lock); +} + +static int mpc8xxx_spi_setup(struct spi_device *spi) +{ + struct mpc8xxx_spi *mpc8xxx_spi; + int retval; + u32 hw_mode; + struct spi_mpc8xxx_cs *cs = spi->controller_state; + + if (!spi->max_speed_hz) + return -EINVAL; + + if (!cs) { + cs = kzalloc(sizeof *cs, GFP_KERNEL); + if (!cs) + return -ENOMEM; + spi->controller_state = cs; + } + mpc8xxx_spi = spi_master_get_devdata(spi->master); + + hw_mode = cs->hw_mode; /* Save original settings */ + cs->hw_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode); + /* mask out bits we are going to set */ + cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH + | SPMODE_REV | SPMODE_LOOP); + + if (spi->mode & SPI_CPHA) + cs->hw_mode |= SPMODE_CP_BEGIN_EDGECLK; + if (spi->mode & SPI_CPOL) + cs->hw_mode |= SPMODE_CI_INACTIVEHIGH; + if (!(spi->mode & SPI_LSB_FIRST)) + cs->hw_mode |= SPMODE_REV; + if (spi->mode & SPI_LOOP) + cs->hw_mode |= SPMODE_LOOP; + + retval = mpc8xxx_spi_setup_transfer(spi, NULL); + if (retval < 0) { + cs->hw_mode = hw_mode; /* Restore settings */ + return retval; + } + return 0; +} + +static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) +{ + u16 len; + + dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__, + in_be16(&mspi->rx_bd->cbd_datlen), mspi->count); + + len = in_be16(&mspi->rx_bd->cbd_datlen); + if (len > mspi->count) { + WARN_ON(1); + len = mspi->count; + } + + /* Clear the events */ + mpc8xxx_spi_write_reg(&mspi->base->event, events); + + mspi->count -= len; + if (mspi->count) + mpc8xxx_spi_cpm_bufs_start(mspi); + else + complete(&mspi->done); +} + +static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) +{ + /* We need handle RX first */ + if (events & SPIE_NE) { + u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive); + + if (mspi->rx) + mspi->get_rx(rx_data, mspi); + } + + if ((events & SPIE_NF) == 0) + /* spin until TX is done */ + while (((events = + mpc8xxx_spi_read_reg(&mspi->base->event)) & + SPIE_NF) == 0) + cpu_relax(); + + /* Clear the events */ + mpc8xxx_spi_write_reg(&mspi->base->event, events); + + mspi->count -= 1; + if (mspi->count) { + u32 word = mspi->get_tx(mspi); + + mpc8xxx_spi_write_reg(&mspi->base->transmit, word); + } else { + complete(&mspi->done); + } +} + +static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data) +{ + struct mpc8xxx_spi *mspi = context_data; + irqreturn_t ret = IRQ_NONE; + u32 events; + + /* Get interrupt events(tx/rx) */ + events = mpc8xxx_spi_read_reg(&mspi->base->event); + if (events) + ret = IRQ_HANDLED; + + dev_dbg(mspi->dev, "%s: events %x\n", __func__, events); + + if (mspi->flags & SPI_CPM_MODE) + mpc8xxx_spi_cpm_irq(mspi, events); + else + mpc8xxx_spi_cpu_irq(mspi, events); + + return ret; +} + +static int mpc8xxx_spi_transfer(struct spi_device *spi, + struct spi_message *m) +{ + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + unsigned long flags; + + m->actual_length = 0; + m->status = -EINPROGRESS; + + spin_lock_irqsave(&mpc8xxx_spi->lock, flags); + list_add_tail(&m->queue, &mpc8xxx_spi->queue); + queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work); + spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags); + + return 0; +} + + +static void mpc8xxx_spi_cleanup(struct spi_device *spi) +{ + kfree(spi->controller_state); +} + +static void *mpc8xxx_spi_alloc_dummy_rx(void) +{ + mutex_lock(&mpc8xxx_dummy_rx_lock); + + if (!mpc8xxx_dummy_rx) + mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL); + if (mpc8xxx_dummy_rx) + mpc8xxx_dummy_rx_refcnt++; + + mutex_unlock(&mpc8xxx_dummy_rx_lock); + + return mpc8xxx_dummy_rx; +} + +static void mpc8xxx_spi_free_dummy_rx(void) +{ + mutex_lock(&mpc8xxx_dummy_rx_lock); + + switch (mpc8xxx_dummy_rx_refcnt) { + case 0: + WARN_ON(1); + break; + case 1: + kfree(mpc8xxx_dummy_rx); + mpc8xxx_dummy_rx = NULL; + /* fall through */ + default: + mpc8xxx_dummy_rx_refcnt--; + break; + } + + mutex_unlock(&mpc8xxx_dummy_rx_lock); +} + +static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi) +{ + struct device *dev = mspi->dev; + struct device_node *np = dev->of_node; + const u32 *iprop; + int size; + unsigned long spi_base_ofs; + unsigned long pram_ofs = -ENOMEM; + + /* Can't use of_address_to_resource(), QE muram isn't at 0. */ + iprop = of_get_property(np, "reg", &size); + + /* QE with a fixed pram location? */ + if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4) + return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE); + + /* QE but with a dynamic pram location? */ + if (mspi->flags & SPI_QE) { + pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64); + qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock, + QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs); + return pram_ofs; + } + + /* CPM1 and CPM2 pram must be at a fixed addr. */ + if (!iprop || size != sizeof(*iprop) * 4) + return -ENOMEM; + + spi_base_ofs = cpm_muram_alloc_fixed(iprop[2], 2); + if (IS_ERR_VALUE(spi_base_ofs)) + return -ENOMEM; + + if (mspi->flags & SPI_CPM2) { + pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64); + if (!IS_ERR_VALUE(pram_ofs)) { + u16 __iomem *spi_base = cpm_muram_addr(spi_base_ofs); + + out_be16(spi_base, pram_ofs); + } + } else { + struct spi_pram __iomem *pram = cpm_muram_addr(spi_base_ofs); + u16 rpbase = in_be16(&pram->rpbase); + + /* Microcode relocation patch applied? */ + if (rpbase) + pram_ofs = rpbase; + else + return spi_base_ofs; + } + + cpm_muram_free(spi_base_ofs); + return pram_ofs; +} + +static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi) +{ + struct device *dev = mspi->dev; + struct device_node *np = dev->of_node; + const u32 *iprop; + int size; + unsigned long pram_ofs; + unsigned long bds_ofs; + + if (!(mspi->flags & SPI_CPM_MODE)) + return 0; + + if (!mpc8xxx_spi_alloc_dummy_rx()) + return -ENOMEM; + + if (mspi->flags & SPI_QE) { + iprop = of_get_property(np, "cell-index", &size); + if (iprop && size == sizeof(*iprop)) + mspi->subblock = *iprop; + + switch (mspi->subblock) { + default: + dev_warn(dev, "cell-index unspecified, assuming SPI1"); + /* fall through */ + case 0: + mspi->subblock = QE_CR_SUBBLOCK_SPI1; + break; + case 1: + mspi->subblock = QE_CR_SUBBLOCK_SPI2; + break; + } + } + + pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi); + if (IS_ERR_VALUE(pram_ofs)) { + dev_err(dev, "can't allocate spi parameter ram\n"); + goto err_pram; + } + + bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) + + sizeof(*mspi->rx_bd), 8); + if (IS_ERR_VALUE(bds_ofs)) { + dev_err(dev, "can't allocate bds\n"); + goto err_bds; + } + + mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, mspi->dma_dummy_tx)) { + dev_err(dev, "unable to map dummy tx buffer\n"); + goto err_dummy_tx; + } + + mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR, + DMA_FROM_DEVICE); + if (dma_mapping_error(dev, mspi->dma_dummy_rx)) { + dev_err(dev, "unable to map dummy rx buffer\n"); + goto err_dummy_rx; + } + + mspi->pram = cpm_muram_addr(pram_ofs); + + mspi->tx_bd = cpm_muram_addr(bds_ofs); + mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd)); + + /* Initialize parameter ram. */ + out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd)); + out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd)); + out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL); + out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL); + out_be16(&mspi->pram->mrblr, SPI_MRBLR); + out_be32(&mspi->pram->rstate, 0); + out_be32(&mspi->pram->rdp, 0); + out_be16(&mspi->pram->rbptr, 0); + out_be16(&mspi->pram->rbc, 0); + out_be32(&mspi->pram->rxtmp, 0); + out_be32(&mspi->pram->tstate, 0); + out_be32(&mspi->pram->tdp, 0); + out_be16(&mspi->pram->tbptr, 0); + out_be16(&mspi->pram->tbc, 0); + out_be32(&mspi->pram->txtmp, 0); + + return 0; + +err_dummy_rx: + dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE); +err_dummy_tx: + cpm_muram_free(bds_ofs); +err_bds: + cpm_muram_free(pram_ofs); +err_pram: + mpc8xxx_spi_free_dummy_rx(); + return -ENOMEM; +} + +static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi) +{ + struct device *dev = mspi->dev; + + dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE); + dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE); + cpm_muram_free(cpm_muram_offset(mspi->tx_bd)); + cpm_muram_free(cpm_muram_offset(mspi->pram)); + mpc8xxx_spi_free_dummy_rx(); +} + +static const char *mpc8xxx_spi_strmode(unsigned int flags) +{ + if (flags & SPI_QE_CPU_MODE) { + return "QE CPU"; + } else if (flags & SPI_CPM_MODE) { + if (flags & SPI_QE) + return "QE"; + else if (flags & SPI_CPM2) + return "CPM2"; + else + return "CPM1"; + } + return "CPU"; +} + +static struct spi_master * __devinit +mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) +{ + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct spi_master *master; + struct mpc8xxx_spi *mpc8xxx_spi; + u32 regval; + int ret = 0; + + master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi)); + if (master == NULL) { + ret = -ENOMEM; + goto err; + } + + dev_set_drvdata(dev, master); + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH + | SPI_LSB_FIRST | SPI_LOOP; + + master->setup = mpc8xxx_spi_setup; + master->transfer = mpc8xxx_spi_transfer; + master->cleanup = mpc8xxx_spi_cleanup; + master->dev.of_node = dev->of_node; + + mpc8xxx_spi = spi_master_get_devdata(master); + mpc8xxx_spi->dev = dev; + mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8; + mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8; + mpc8xxx_spi->flags = pdata->flags; + mpc8xxx_spi->spibrg = pdata->sysclk; + + ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi); + if (ret) + goto err_cpm_init; + + mpc8xxx_spi->rx_shift = 0; + mpc8xxx_spi->tx_shift = 0; + if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { + mpc8xxx_spi->rx_shift = 16; + mpc8xxx_spi->tx_shift = 24; + } + + init_completion(&mpc8xxx_spi->done); + + mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem)); + if (mpc8xxx_spi->base == NULL) { + ret = -ENOMEM; + goto err_ioremap; + } + + mpc8xxx_spi->irq = irq; + + /* Register for SPI Interrupt */ + ret = request_irq(mpc8xxx_spi->irq, mpc8xxx_spi_irq, + 0, "mpc8xxx_spi", mpc8xxx_spi); + + if (ret != 0) + goto unmap_io; + + master->bus_num = pdata->bus_num; + master->num_chipselect = pdata->max_chipselect; + + /* SPI controller initializations */ + mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, 0); + mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0); + mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->command, 0); + mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, 0xffffffff); + + /* Enable SPI interface */ + regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; + if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) + regval |= SPMODE_OP; + + mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval); + spin_lock_init(&mpc8xxx_spi->lock); + init_completion(&mpc8xxx_spi->done); + INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work); + INIT_LIST_HEAD(&mpc8xxx_spi->queue); + + mpc8xxx_spi->workqueue = create_singlethread_workqueue( + dev_name(master->dev.parent)); + if (mpc8xxx_spi->workqueue == NULL) { + ret = -EBUSY; + goto free_irq; + } + + ret = spi_register_master(master); + if (ret < 0) + goto unreg_master; + + dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base, + mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags)); + + return master; + +unreg_master: + destroy_workqueue(mpc8xxx_spi->workqueue); +free_irq: + free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); +unmap_io: + iounmap(mpc8xxx_spi->base); +err_ioremap: + mpc8xxx_spi_cpm_free(mpc8xxx_spi); +err_cpm_init: + spi_master_put(master); +err: + return ERR_PTR(ret); +} + +static int __devexit mpc8xxx_spi_remove(struct device *dev) +{ + struct mpc8xxx_spi *mpc8xxx_spi; + struct spi_master *master; + + master = dev_get_drvdata(dev); + mpc8xxx_spi = spi_master_get_devdata(master); + + flush_workqueue(mpc8xxx_spi->workqueue); + destroy_workqueue(mpc8xxx_spi->workqueue); + spi_unregister_master(master); + + free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); + iounmap(mpc8xxx_spi->base); + mpc8xxx_spi_cpm_free(mpc8xxx_spi); + + return 0; +} + +struct mpc8xxx_spi_probe_info { + struct fsl_spi_platform_data pdata; + int *gpios; + bool *alow_flags; +}; + +static struct mpc8xxx_spi_probe_info * +to_of_pinfo(struct fsl_spi_platform_data *pdata) +{ + return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata); +} + +static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on) +{ + struct device *dev = spi->dev.parent; + struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data); + u16 cs = spi->chip_select; + int gpio = pinfo->gpios[cs]; + bool alow = pinfo->alow_flags[cs]; + + gpio_set_value(gpio, on ^ alow); +} + +static int of_mpc8xxx_spi_get_chipselects(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); + unsigned int ngpios; + int i = 0; + int ret; + + ngpios = of_gpio_count(np); + if (!ngpios) { + /* + * SPI w/o chip-select line. One SPI device is still permitted + * though. + */ + pdata->max_chipselect = 1; + return 0; + } + + pinfo->gpios = kmalloc(ngpios * sizeof(*pinfo->gpios), GFP_KERNEL); + if (!pinfo->gpios) + return -ENOMEM; + memset(pinfo->gpios, -1, ngpios * sizeof(*pinfo->gpios)); + + pinfo->alow_flags = kzalloc(ngpios * sizeof(*pinfo->alow_flags), + GFP_KERNEL); + if (!pinfo->alow_flags) { + ret = -ENOMEM; + goto err_alloc_flags; + } + + for (; i < ngpios; i++) { + int gpio; + enum of_gpio_flags flags; + + gpio = of_get_gpio_flags(np, i, &flags); + if (!gpio_is_valid(gpio)) { + dev_err(dev, "invalid gpio #%d: %d\n", i, gpio); + ret = gpio; + goto err_loop; + } + + ret = gpio_request(gpio, dev_name(dev)); + if (ret) { + dev_err(dev, "can't request gpio #%d: %d\n", i, ret); + goto err_loop; + } + + pinfo->gpios[i] = gpio; + pinfo->alow_flags[i] = flags & OF_GPIO_ACTIVE_LOW; + + ret = gpio_direction_output(pinfo->gpios[i], + pinfo->alow_flags[i]); + if (ret) { + dev_err(dev, "can't set output direction for gpio " + "#%d: %d\n", i, ret); + goto err_loop; + } + } + + pdata->max_chipselect = ngpios; + pdata->cs_control = mpc8xxx_spi_cs_control; + + return 0; + +err_loop: + while (i >= 0) { + if (gpio_is_valid(pinfo->gpios[i])) + gpio_free(pinfo->gpios[i]); + i--; + } + + kfree(pinfo->alow_flags); + pinfo->alow_flags = NULL; +err_alloc_flags: + kfree(pinfo->gpios); + pinfo->gpios = NULL; + return ret; +} + +static int of_mpc8xxx_spi_free_chipselects(struct device *dev) +{ + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); + int i; + + if (!pinfo->gpios) + return 0; + + for (i = 0; i < pdata->max_chipselect; i++) { + if (gpio_is_valid(pinfo->gpios[i])) + gpio_free(pinfo->gpios[i]); + } + + kfree(pinfo->gpios); + kfree(pinfo->alow_flags); + return 0; +} + +static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev, + const struct of_device_id *ofid) +{ + struct device *dev = &ofdev->dev; + struct device_node *np = ofdev->dev.of_node; + struct mpc8xxx_spi_probe_info *pinfo; + struct fsl_spi_platform_data *pdata; + struct spi_master *master; + struct resource mem; + struct resource irq; + const void *prop; + int ret = -ENOMEM; + + pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); + if (!pinfo) + return -ENOMEM; + + pdata = &pinfo->pdata; + dev->platform_data = pdata; + + /* Allocate bus num dynamically. */ + pdata->bus_num = -1; + + /* SPI controller is either clocked from QE or SoC clock. */ + pdata->sysclk = get_brgfreq(); + if (pdata->sysclk == -1) { + pdata->sysclk = fsl_get_sys_freq(); + if (pdata->sysclk == -1) { + ret = -ENODEV; + goto err_clk; + } + } + + prop = of_get_property(np, "mode", NULL); + if (prop && !strcmp(prop, "cpu-qe")) + pdata->flags = SPI_QE_CPU_MODE; + else if (prop && !strcmp(prop, "qe")) + pdata->flags = SPI_CPM_MODE | SPI_QE; + else if (of_device_is_compatible(np, "fsl,cpm2-spi")) + pdata->flags = SPI_CPM_MODE | SPI_CPM2; + else if (of_device_is_compatible(np, "fsl,cpm1-spi")) + pdata->flags = SPI_CPM_MODE | SPI_CPM1; + + ret = of_mpc8xxx_spi_get_chipselects(dev); + if (ret) + goto err; + + ret = of_address_to_resource(np, 0, &mem); + if (ret) + goto err; + + ret = of_irq_to_resource(np, 0, &irq); + if (!ret) { + ret = -EINVAL; + goto err; + } + + master = mpc8xxx_spi_probe(dev, &mem, irq.start); + if (IS_ERR(master)) { + ret = PTR_ERR(master); + goto err; + } + + return 0; + +err: + of_mpc8xxx_spi_free_chipselects(dev); +err_clk: + kfree(pinfo); + return ret; +} + +static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev) +{ + int ret; + + ret = mpc8xxx_spi_remove(&ofdev->dev); + if (ret) + return ret; + of_mpc8xxx_spi_free_chipselects(&ofdev->dev); + return 0; +} + +static const struct of_device_id of_mpc8xxx_spi_match[] = { + { .compatible = "fsl,spi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_mpc8xxx_spi_match); + +static struct of_platform_driver of_mpc8xxx_spi_driver = { + .driver = { + .name = "mpc8xxx_spi", + .owner = THIS_MODULE, + .of_match_table = of_mpc8xxx_spi_match, + }, + .probe = of_mpc8xxx_spi_probe, + .remove = __devexit_p(of_mpc8xxx_spi_remove), +}; + +#ifdef CONFIG_MPC832x_RDB +/* + * XXX XXX XXX + * This is "legacy" platform driver, was used by the MPC8323E-RDB boards + * only. The driver should go away soon, since newer MPC8323E-RDB's device + * tree can work with OpenFirmware driver. But for now we support old trees + * as well. + */ +static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev) +{ + struct resource *mem; + int irq; + struct spi_master *master; + + if (!pdev->dev.platform_data) + return -EINVAL; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -EINVAL; + + master = mpc8xxx_spi_probe(&pdev->dev, mem, irq); + if (IS_ERR(master)) + return PTR_ERR(master); + return 0; +} + +static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev) +{ + return mpc8xxx_spi_remove(&pdev->dev); +} + +MODULE_ALIAS("platform:mpc8xxx_spi"); +static struct platform_driver mpc8xxx_spi_driver = { + .probe = plat_mpc8xxx_spi_probe, + .remove = __devexit_p(plat_mpc8xxx_spi_remove), + .driver = { + .name = "mpc8xxx_spi", + .owner = THIS_MODULE, + }, +}; + +static bool legacy_driver_failed; + +static void __init legacy_driver_register(void) +{ + legacy_driver_failed = platform_driver_register(&mpc8xxx_spi_driver); +} + +static void __exit legacy_driver_unregister(void) +{ + if (legacy_driver_failed) + return; + platform_driver_unregister(&mpc8xxx_spi_driver); +} +#else +static void __init legacy_driver_register(void) {} +static void __exit legacy_driver_unregister(void) {} +#endif /* CONFIG_MPC832x_RDB */ + +static int __init mpc8xxx_spi_init(void) +{ + legacy_driver_register(); + return of_register_platform_driver(&of_mpc8xxx_spi_driver); +} + +static void __exit mpc8xxx_spi_exit(void) +{ + of_unregister_platform_driver(&of_mpc8xxx_spi_driver); + legacy_driver_unregister(); +} + +module_init(mpc8xxx_spi_init); +module_exit(mpc8xxx_spi_exit); + +MODULE_AUTHOR("Kumar Gala"); +MODULE_DESCRIPTION("Simple MPC8xxx SPI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c deleted file mode 100644 index 1dd86b835cd8..000000000000 --- a/drivers/spi/spi_mpc8xxx.c +++ /dev/null @@ -1,1425 +0,0 @@ -/* - * MPC8xxx SPI controller driver. - * - * Maintainer: Kumar Gala - * - * Copyright (C) 2006 Polycom, Inc. - * - * CPM SPI and QE buffer descriptors mode support: - * Copyright (c) 2009 MontaVista Software, Inc. - * Author: Anton Vorontsov - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* CPM1 and CPM2 are mutually exclusive. */ -#ifdef CONFIG_CPM1 -#include -#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0) -#else -#include -#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0) -#endif - -/* SPI Controller registers */ -struct mpc8xxx_spi_reg { - u8 res1[0x20]; - __be32 mode; - __be32 event; - __be32 mask; - __be32 command; - __be32 transmit; - __be32 receive; -}; - -/* SPI Controller mode register definitions */ -#define SPMODE_LOOP (1 << 30) -#define SPMODE_CI_INACTIVEHIGH (1 << 29) -#define SPMODE_CP_BEGIN_EDGECLK (1 << 28) -#define SPMODE_DIV16 (1 << 27) -#define SPMODE_REV (1 << 26) -#define SPMODE_MS (1 << 25) -#define SPMODE_ENABLE (1 << 24) -#define SPMODE_LEN(x) ((x) << 20) -#define SPMODE_PM(x) ((x) << 16) -#define SPMODE_OP (1 << 14) -#define SPMODE_CG(x) ((x) << 7) - -/* - * Default for SPI Mode: - * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk - */ -#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \ - SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf)) - -/* SPIE register values */ -#define SPIE_NE 0x00000200 /* Not empty */ -#define SPIE_NF 0x00000100 /* Not full */ - -/* SPIM register values */ -#define SPIM_NE 0x00000200 /* Not empty */ -#define SPIM_NF 0x00000100 /* Not full */ - -#define SPIE_TXB 0x00000200 /* Last char is written to tx fifo */ -#define SPIE_RXB 0x00000100 /* Last char is written to rx buf */ - -/* SPCOM register values */ -#define SPCOM_STR (1 << 23) /* Start transmit */ - -#define SPI_PRAM_SIZE 0x100 -#define SPI_MRBLR ((unsigned int)PAGE_SIZE) - -/* SPI Controller driver's private data. */ -struct mpc8xxx_spi { - struct device *dev; - struct mpc8xxx_spi_reg __iomem *base; - - /* rx & tx bufs from the spi_transfer */ - const void *tx; - void *rx; - - int subblock; - struct spi_pram __iomem *pram; - struct cpm_buf_desc __iomem *tx_bd; - struct cpm_buf_desc __iomem *rx_bd; - - struct spi_transfer *xfer_in_progress; - - /* dma addresses for CPM transfers */ - dma_addr_t tx_dma; - dma_addr_t rx_dma; - bool map_tx_dma; - bool map_rx_dma; - - dma_addr_t dma_dummy_tx; - dma_addr_t dma_dummy_rx; - - /* functions to deal with different sized buffers */ - void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *); - u32(*get_tx) (struct mpc8xxx_spi *); - - unsigned int count; - unsigned int irq; - - unsigned nsecs; /* (clock cycle time)/2 */ - - u32 spibrg; /* SPIBRG input clock */ - u32 rx_shift; /* RX data reg shift when in qe mode */ - u32 tx_shift; /* TX data reg shift when in qe mode */ - - unsigned int flags; - - struct workqueue_struct *workqueue; - struct work_struct work; - - struct list_head queue; - spinlock_t lock; - - struct completion done; -}; - -static void *mpc8xxx_dummy_rx; -static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock); -static int mpc8xxx_dummy_rx_refcnt; - -struct spi_mpc8xxx_cs { - /* functions to deal with different sized buffers */ - void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *); - u32 (*get_tx) (struct mpc8xxx_spi *); - u32 rx_shift; /* RX data reg shift when in qe mode */ - u32 tx_shift; /* TX data reg shift when in qe mode */ - u32 hw_mode; /* Holds HW mode register settings */ -}; - -static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val) -{ - out_be32(reg, val); -} - -static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg) -{ - return in_be32(reg); -} - -#define MPC83XX_SPI_RX_BUF(type) \ -static \ -void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \ -{ \ - type *rx = mpc8xxx_spi->rx; \ - *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \ - mpc8xxx_spi->rx = rx; \ -} - -#define MPC83XX_SPI_TX_BUF(type) \ -static \ -u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \ -{ \ - u32 data; \ - const type *tx = mpc8xxx_spi->tx; \ - if (!tx) \ - return 0; \ - data = *tx++ << mpc8xxx_spi->tx_shift; \ - mpc8xxx_spi->tx = tx; \ - return data; \ -} - -MPC83XX_SPI_RX_BUF(u8) -MPC83XX_SPI_RX_BUF(u16) -MPC83XX_SPI_RX_BUF(u32) -MPC83XX_SPI_TX_BUF(u8) -MPC83XX_SPI_TX_BUF(u16) -MPC83XX_SPI_TX_BUF(u32) - -static void mpc8xxx_spi_change_mode(struct spi_device *spi) -{ - struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); - struct spi_mpc8xxx_cs *cs = spi->controller_state; - __be32 __iomem *mode = &mspi->base->mode; - unsigned long flags; - - if (cs->hw_mode == mpc8xxx_spi_read_reg(mode)) - return; - - /* Turn off IRQs locally to minimize time that SPI is disabled. */ - local_irq_save(flags); - - /* Turn off SPI unit prior changing mode */ - mpc8xxx_spi_write_reg(mode, cs->hw_mode & ~SPMODE_ENABLE); - - /* When in CPM mode, we need to reinit tx and rx. */ - if (mspi->flags & SPI_CPM_MODE) { - if (mspi->flags & SPI_QE) { - qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock, - QE_CR_PROTOCOL_UNSPECIFIED, 0); - } else { - cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX); - if (mspi->flags & SPI_CPM1) { - out_be16(&mspi->pram->rbptr, - in_be16(&mspi->pram->rbase)); - out_be16(&mspi->pram->tbptr, - in_be16(&mspi->pram->tbase)); - } - } - } - mpc8xxx_spi_write_reg(mode, cs->hw_mode); - local_irq_restore(flags); -} - -static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value) -{ - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); - struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data; - bool pol = spi->mode & SPI_CS_HIGH; - struct spi_mpc8xxx_cs *cs = spi->controller_state; - - if (value == BITBANG_CS_INACTIVE) { - if (pdata->cs_control) - pdata->cs_control(spi, !pol); - } - - if (value == BITBANG_CS_ACTIVE) { - mpc8xxx_spi->rx_shift = cs->rx_shift; - mpc8xxx_spi->tx_shift = cs->tx_shift; - mpc8xxx_spi->get_rx = cs->get_rx; - mpc8xxx_spi->get_tx = cs->get_tx; - - mpc8xxx_spi_change_mode(spi); - - if (pdata->cs_control) - pdata->cs_control(spi, pol); - } -} - -static int -mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs, - struct spi_device *spi, - struct mpc8xxx_spi *mpc8xxx_spi, - int bits_per_word) -{ - cs->rx_shift = 0; - cs->tx_shift = 0; - if (bits_per_word <= 8) { - cs->get_rx = mpc8xxx_spi_rx_buf_u8; - cs->get_tx = mpc8xxx_spi_tx_buf_u8; - if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { - cs->rx_shift = 16; - cs->tx_shift = 24; - } - } else if (bits_per_word <= 16) { - cs->get_rx = mpc8xxx_spi_rx_buf_u16; - cs->get_tx = mpc8xxx_spi_tx_buf_u16; - if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { - cs->rx_shift = 16; - cs->tx_shift = 16; - } - } else if (bits_per_word <= 32) { - cs->get_rx = mpc8xxx_spi_rx_buf_u32; - cs->get_tx = mpc8xxx_spi_tx_buf_u32; - } else - return -EINVAL; - - if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE && - spi->mode & SPI_LSB_FIRST) { - cs->tx_shift = 0; - if (bits_per_word <= 8) - cs->rx_shift = 8; - else - cs->rx_shift = 0; - } - mpc8xxx_spi->rx_shift = cs->rx_shift; - mpc8xxx_spi->tx_shift = cs->tx_shift; - mpc8xxx_spi->get_rx = cs->get_rx; - mpc8xxx_spi->get_tx = cs->get_tx; - - return bits_per_word; -} - -static int -mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs, - struct spi_device *spi, - int bits_per_word) -{ - /* QE uses Little Endian for words > 8 - * so transform all words > 8 into 8 bits - * Unfortnatly that doesn't work for LSB so - * reject these for now */ - /* Note: 32 bits word, LSB works iff - * tfcr/rfcr is set to CPMFCR_GBL */ - if (spi->mode & SPI_LSB_FIRST && - bits_per_word > 8) - return -EINVAL; - if (bits_per_word > 8) - return 8; /* pretend its 8 bits */ - return bits_per_word; -} - -static -int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) -{ - struct mpc8xxx_spi *mpc8xxx_spi; - int bits_per_word; - u8 pm; - u32 hz; - struct spi_mpc8xxx_cs *cs = spi->controller_state; - - mpc8xxx_spi = spi_master_get_devdata(spi->master); - - if (t) { - bits_per_word = t->bits_per_word; - hz = t->speed_hz; - } else { - bits_per_word = 0; - hz = 0; - } - - /* spi_transfer level calls that work per-word */ - if (!bits_per_word) - bits_per_word = spi->bits_per_word; - - /* Make sure its a bit width we support [4..16, 32] */ - if ((bits_per_word < 4) - || ((bits_per_word > 16) && (bits_per_word != 32))) - return -EINVAL; - - if (!hz) - hz = spi->max_speed_hz; - - if (!(mpc8xxx_spi->flags & SPI_CPM_MODE)) - bits_per_word = mspi_apply_cpu_mode_quirks(cs, spi, - mpc8xxx_spi, - bits_per_word); - else if (mpc8xxx_spi->flags & SPI_QE) - bits_per_word = mspi_apply_qe_mode_quirks(cs, spi, - bits_per_word); - - if (bits_per_word < 0) - return bits_per_word; - - if (bits_per_word == 32) - bits_per_word = 0; - else - bits_per_word = bits_per_word - 1; - - /* mask out bits we are going to set */ - cs->hw_mode &= ~(SPMODE_LEN(0xF) | SPMODE_DIV16 - | SPMODE_PM(0xF)); - - cs->hw_mode |= SPMODE_LEN(bits_per_word); - - if ((mpc8xxx_spi->spibrg / hz) > 64) { - cs->hw_mode |= SPMODE_DIV16; - pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1; - - WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. " - "Will use %d Hz instead.\n", dev_name(&spi->dev), - hz, mpc8xxx_spi->spibrg / 1024); - if (pm > 16) - pm = 16; - } else - pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1; - if (pm) - pm--; - - cs->hw_mode |= SPMODE_PM(pm); - - mpc8xxx_spi_change_mode(spi); - return 0; -} - -static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi) -{ - struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd; - struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd; - unsigned int xfer_len = min(mspi->count, SPI_MRBLR); - unsigned int xfer_ofs; - - xfer_ofs = mspi->xfer_in_progress->len - mspi->count; - - if (mspi->rx_dma == mspi->dma_dummy_rx) - out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma); - else - out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs); - out_be16(&rx_bd->cbd_datlen, 0); - out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP); - - if (mspi->tx_dma == mspi->dma_dummy_tx) - out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma); - else - out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs); - out_be16(&tx_bd->cbd_datlen, xfer_len); - out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP | - BD_SC_LAST); - - /* start transfer */ - mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR); -} - -static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi, - struct spi_transfer *t, bool is_dma_mapped) -{ - struct device *dev = mspi->dev; - - if (is_dma_mapped) { - mspi->map_tx_dma = 0; - mspi->map_rx_dma = 0; - } else { - mspi->map_tx_dma = 1; - mspi->map_rx_dma = 1; - } - - if (!t->tx_buf) { - mspi->tx_dma = mspi->dma_dummy_tx; - mspi->map_tx_dma = 0; - } - - if (!t->rx_buf) { - mspi->rx_dma = mspi->dma_dummy_rx; - mspi->map_rx_dma = 0; - } - - if (mspi->map_tx_dma) { - void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */ - - mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, mspi->tx_dma)) { - dev_err(dev, "unable to map tx dma\n"); - return -ENOMEM; - } - } else if (t->tx_buf) { - mspi->tx_dma = t->tx_dma; - } - - if (mspi->map_rx_dma) { - mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, mspi->rx_dma)) { - dev_err(dev, "unable to map rx dma\n"); - goto err_rx_dma; - } - } else if (t->rx_buf) { - mspi->rx_dma = t->rx_dma; - } - - /* enable rx ints */ - mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB); - - mspi->xfer_in_progress = t; - mspi->count = t->len; - - /* start CPM transfers */ - mpc8xxx_spi_cpm_bufs_start(mspi); - - return 0; - -err_rx_dma: - if (mspi->map_tx_dma) - dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE); - return -ENOMEM; -} - -static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) -{ - struct device *dev = mspi->dev; - struct spi_transfer *t = mspi->xfer_in_progress; - - if (mspi->map_tx_dma) - dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE); - if (mspi->map_rx_dma) - dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE); - mspi->xfer_in_progress = NULL; -} - -static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi, - struct spi_transfer *t, unsigned int len) -{ - u32 word; - - mspi->count = len; - - /* enable rx ints */ - mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE); - - /* transmit word */ - word = mspi->get_tx(mspi); - mpc8xxx_spi_write_reg(&mspi->base->transmit, word); - - return 0; -} - -static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t, - bool is_dma_mapped) -{ - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); - unsigned int len = t->len; - u8 bits_per_word; - int ret; - - bits_per_word = spi->bits_per_word; - if (t->bits_per_word) - bits_per_word = t->bits_per_word; - - if (bits_per_word > 8) { - /* invalid length? */ - if (len & 1) - return -EINVAL; - len /= 2; - } - if (bits_per_word > 16) { - /* invalid length? */ - if (len & 1) - return -EINVAL; - len /= 2; - } - - mpc8xxx_spi->tx = t->tx_buf; - mpc8xxx_spi->rx = t->rx_buf; - - INIT_COMPLETION(mpc8xxx_spi->done); - - if (mpc8xxx_spi->flags & SPI_CPM_MODE) - ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped); - else - ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len); - if (ret) - return ret; - - wait_for_completion(&mpc8xxx_spi->done); - - /* disable rx ints */ - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0); - - if (mpc8xxx_spi->flags & SPI_CPM_MODE) - mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi); - - return mpc8xxx_spi->count; -} - -static void mpc8xxx_spi_do_one_msg(struct spi_message *m) -{ - struct spi_device *spi = m->spi; - struct spi_transfer *t; - unsigned int cs_change; - const int nsecs = 50; - int status; - - cs_change = 1; - status = 0; - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->bits_per_word || t->speed_hz) { - /* Don't allow changes if CS is active */ - status = -EINVAL; - - if (cs_change) - status = mpc8xxx_spi_setup_transfer(spi, t); - if (status < 0) - break; - } - - if (cs_change) { - mpc8xxx_spi_chipselect(spi, BITBANG_CS_ACTIVE); - ndelay(nsecs); - } - cs_change = t->cs_change; - if (t->len) - status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped); - if (status) { - status = -EMSGSIZE; - break; - } - m->actual_length += t->len; - - if (t->delay_usecs) - udelay(t->delay_usecs); - - if (cs_change) { - ndelay(nsecs); - mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE); - ndelay(nsecs); - } - } - - m->status = status; - m->complete(m->context); - - if (status || !cs_change) { - ndelay(nsecs); - mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE); - } - - mpc8xxx_spi_setup_transfer(spi, NULL); -} - -static void mpc8xxx_spi_work(struct work_struct *work) -{ - struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi, - work); - - spin_lock_irq(&mpc8xxx_spi->lock); - while (!list_empty(&mpc8xxx_spi->queue)) { - struct spi_message *m = container_of(mpc8xxx_spi->queue.next, - struct spi_message, queue); - - list_del_init(&m->queue); - spin_unlock_irq(&mpc8xxx_spi->lock); - - mpc8xxx_spi_do_one_msg(m); - - spin_lock_irq(&mpc8xxx_spi->lock); - } - spin_unlock_irq(&mpc8xxx_spi->lock); -} - -static int mpc8xxx_spi_setup(struct spi_device *spi) -{ - struct mpc8xxx_spi *mpc8xxx_spi; - int retval; - u32 hw_mode; - struct spi_mpc8xxx_cs *cs = spi->controller_state; - - if (!spi->max_speed_hz) - return -EINVAL; - - if (!cs) { - cs = kzalloc(sizeof *cs, GFP_KERNEL); - if (!cs) - return -ENOMEM; - spi->controller_state = cs; - } - mpc8xxx_spi = spi_master_get_devdata(spi->master); - - hw_mode = cs->hw_mode; /* Save original settings */ - cs->hw_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode); - /* mask out bits we are going to set */ - cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH - | SPMODE_REV | SPMODE_LOOP); - - if (spi->mode & SPI_CPHA) - cs->hw_mode |= SPMODE_CP_BEGIN_EDGECLK; - if (spi->mode & SPI_CPOL) - cs->hw_mode |= SPMODE_CI_INACTIVEHIGH; - if (!(spi->mode & SPI_LSB_FIRST)) - cs->hw_mode |= SPMODE_REV; - if (spi->mode & SPI_LOOP) - cs->hw_mode |= SPMODE_LOOP; - - retval = mpc8xxx_spi_setup_transfer(spi, NULL); - if (retval < 0) { - cs->hw_mode = hw_mode; /* Restore settings */ - return retval; - } - return 0; -} - -static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) -{ - u16 len; - - dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__, - in_be16(&mspi->rx_bd->cbd_datlen), mspi->count); - - len = in_be16(&mspi->rx_bd->cbd_datlen); - if (len > mspi->count) { - WARN_ON(1); - len = mspi->count; - } - - /* Clear the events */ - mpc8xxx_spi_write_reg(&mspi->base->event, events); - - mspi->count -= len; - if (mspi->count) - mpc8xxx_spi_cpm_bufs_start(mspi); - else - complete(&mspi->done); -} - -static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) -{ - /* We need handle RX first */ - if (events & SPIE_NE) { - u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive); - - if (mspi->rx) - mspi->get_rx(rx_data, mspi); - } - - if ((events & SPIE_NF) == 0) - /* spin until TX is done */ - while (((events = - mpc8xxx_spi_read_reg(&mspi->base->event)) & - SPIE_NF) == 0) - cpu_relax(); - - /* Clear the events */ - mpc8xxx_spi_write_reg(&mspi->base->event, events); - - mspi->count -= 1; - if (mspi->count) { - u32 word = mspi->get_tx(mspi); - - mpc8xxx_spi_write_reg(&mspi->base->transmit, word); - } else { - complete(&mspi->done); - } -} - -static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data) -{ - struct mpc8xxx_spi *mspi = context_data; - irqreturn_t ret = IRQ_NONE; - u32 events; - - /* Get interrupt events(tx/rx) */ - events = mpc8xxx_spi_read_reg(&mspi->base->event); - if (events) - ret = IRQ_HANDLED; - - dev_dbg(mspi->dev, "%s: events %x\n", __func__, events); - - if (mspi->flags & SPI_CPM_MODE) - mpc8xxx_spi_cpm_irq(mspi, events); - else - mpc8xxx_spi_cpu_irq(mspi, events); - - return ret; -} - -static int mpc8xxx_spi_transfer(struct spi_device *spi, - struct spi_message *m) -{ - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); - unsigned long flags; - - m->actual_length = 0; - m->status = -EINPROGRESS; - - spin_lock_irqsave(&mpc8xxx_spi->lock, flags); - list_add_tail(&m->queue, &mpc8xxx_spi->queue); - queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work); - spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags); - - return 0; -} - - -static void mpc8xxx_spi_cleanup(struct spi_device *spi) -{ - kfree(spi->controller_state); -} - -static void *mpc8xxx_spi_alloc_dummy_rx(void) -{ - mutex_lock(&mpc8xxx_dummy_rx_lock); - - if (!mpc8xxx_dummy_rx) - mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL); - if (mpc8xxx_dummy_rx) - mpc8xxx_dummy_rx_refcnt++; - - mutex_unlock(&mpc8xxx_dummy_rx_lock); - - return mpc8xxx_dummy_rx; -} - -static void mpc8xxx_spi_free_dummy_rx(void) -{ - mutex_lock(&mpc8xxx_dummy_rx_lock); - - switch (mpc8xxx_dummy_rx_refcnt) { - case 0: - WARN_ON(1); - break; - case 1: - kfree(mpc8xxx_dummy_rx); - mpc8xxx_dummy_rx = NULL; - /* fall through */ - default: - mpc8xxx_dummy_rx_refcnt--; - break; - } - - mutex_unlock(&mpc8xxx_dummy_rx_lock); -} - -static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi) -{ - struct device *dev = mspi->dev; - struct device_node *np = dev->of_node; - const u32 *iprop; - int size; - unsigned long spi_base_ofs; - unsigned long pram_ofs = -ENOMEM; - - /* Can't use of_address_to_resource(), QE muram isn't at 0. */ - iprop = of_get_property(np, "reg", &size); - - /* QE with a fixed pram location? */ - if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4) - return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE); - - /* QE but with a dynamic pram location? */ - if (mspi->flags & SPI_QE) { - pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64); - qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock, - QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs); - return pram_ofs; - } - - /* CPM1 and CPM2 pram must be at a fixed addr. */ - if (!iprop || size != sizeof(*iprop) * 4) - return -ENOMEM; - - spi_base_ofs = cpm_muram_alloc_fixed(iprop[2], 2); - if (IS_ERR_VALUE(spi_base_ofs)) - return -ENOMEM; - - if (mspi->flags & SPI_CPM2) { - pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64); - if (!IS_ERR_VALUE(pram_ofs)) { - u16 __iomem *spi_base = cpm_muram_addr(spi_base_ofs); - - out_be16(spi_base, pram_ofs); - } - } else { - struct spi_pram __iomem *pram = cpm_muram_addr(spi_base_ofs); - u16 rpbase = in_be16(&pram->rpbase); - - /* Microcode relocation patch applied? */ - if (rpbase) - pram_ofs = rpbase; - else - return spi_base_ofs; - } - - cpm_muram_free(spi_base_ofs); - return pram_ofs; -} - -static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi) -{ - struct device *dev = mspi->dev; - struct device_node *np = dev->of_node; - const u32 *iprop; - int size; - unsigned long pram_ofs; - unsigned long bds_ofs; - - if (!(mspi->flags & SPI_CPM_MODE)) - return 0; - - if (!mpc8xxx_spi_alloc_dummy_rx()) - return -ENOMEM; - - if (mspi->flags & SPI_QE) { - iprop = of_get_property(np, "cell-index", &size); - if (iprop && size == sizeof(*iprop)) - mspi->subblock = *iprop; - - switch (mspi->subblock) { - default: - dev_warn(dev, "cell-index unspecified, assuming SPI1"); - /* fall through */ - case 0: - mspi->subblock = QE_CR_SUBBLOCK_SPI1; - break; - case 1: - mspi->subblock = QE_CR_SUBBLOCK_SPI2; - break; - } - } - - pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi); - if (IS_ERR_VALUE(pram_ofs)) { - dev_err(dev, "can't allocate spi parameter ram\n"); - goto err_pram; - } - - bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) + - sizeof(*mspi->rx_bd), 8); - if (IS_ERR_VALUE(bds_ofs)) { - dev_err(dev, "can't allocate bds\n"); - goto err_bds; - } - - mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, mspi->dma_dummy_tx)) { - dev_err(dev, "unable to map dummy tx buffer\n"); - goto err_dummy_tx; - } - - mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR, - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, mspi->dma_dummy_rx)) { - dev_err(dev, "unable to map dummy rx buffer\n"); - goto err_dummy_rx; - } - - mspi->pram = cpm_muram_addr(pram_ofs); - - mspi->tx_bd = cpm_muram_addr(bds_ofs); - mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd)); - - /* Initialize parameter ram. */ - out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd)); - out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd)); - out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL); - out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL); - out_be16(&mspi->pram->mrblr, SPI_MRBLR); - out_be32(&mspi->pram->rstate, 0); - out_be32(&mspi->pram->rdp, 0); - out_be16(&mspi->pram->rbptr, 0); - out_be16(&mspi->pram->rbc, 0); - out_be32(&mspi->pram->rxtmp, 0); - out_be32(&mspi->pram->tstate, 0); - out_be32(&mspi->pram->tdp, 0); - out_be16(&mspi->pram->tbptr, 0); - out_be16(&mspi->pram->tbc, 0); - out_be32(&mspi->pram->txtmp, 0); - - return 0; - -err_dummy_rx: - dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE); -err_dummy_tx: - cpm_muram_free(bds_ofs); -err_bds: - cpm_muram_free(pram_ofs); -err_pram: - mpc8xxx_spi_free_dummy_rx(); - return -ENOMEM; -} - -static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi) -{ - struct device *dev = mspi->dev; - - dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE); - dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE); - cpm_muram_free(cpm_muram_offset(mspi->tx_bd)); - cpm_muram_free(cpm_muram_offset(mspi->pram)); - mpc8xxx_spi_free_dummy_rx(); -} - -static const char *mpc8xxx_spi_strmode(unsigned int flags) -{ - if (flags & SPI_QE_CPU_MODE) { - return "QE CPU"; - } else if (flags & SPI_CPM_MODE) { - if (flags & SPI_QE) - return "QE"; - else if (flags & SPI_CPM2) - return "CPM2"; - else - return "CPM1"; - } - return "CPU"; -} - -static struct spi_master * __devinit -mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) -{ - struct fsl_spi_platform_data *pdata = dev->platform_data; - struct spi_master *master; - struct mpc8xxx_spi *mpc8xxx_spi; - u32 regval; - int ret = 0; - - master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi)); - if (master == NULL) { - ret = -ENOMEM; - goto err; - } - - dev_set_drvdata(dev, master); - - /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH - | SPI_LSB_FIRST | SPI_LOOP; - - master->setup = mpc8xxx_spi_setup; - master->transfer = mpc8xxx_spi_transfer; - master->cleanup = mpc8xxx_spi_cleanup; - master->dev.of_node = dev->of_node; - - mpc8xxx_spi = spi_master_get_devdata(master); - mpc8xxx_spi->dev = dev; - mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8; - mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8; - mpc8xxx_spi->flags = pdata->flags; - mpc8xxx_spi->spibrg = pdata->sysclk; - - ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi); - if (ret) - goto err_cpm_init; - - mpc8xxx_spi->rx_shift = 0; - mpc8xxx_spi->tx_shift = 0; - if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { - mpc8xxx_spi->rx_shift = 16; - mpc8xxx_spi->tx_shift = 24; - } - - init_completion(&mpc8xxx_spi->done); - - mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem)); - if (mpc8xxx_spi->base == NULL) { - ret = -ENOMEM; - goto err_ioremap; - } - - mpc8xxx_spi->irq = irq; - - /* Register for SPI Interrupt */ - ret = request_irq(mpc8xxx_spi->irq, mpc8xxx_spi_irq, - 0, "mpc8xxx_spi", mpc8xxx_spi); - - if (ret != 0) - goto unmap_io; - - master->bus_num = pdata->bus_num; - master->num_chipselect = pdata->max_chipselect; - - /* SPI controller initializations */ - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, 0); - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0); - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->command, 0); - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, 0xffffffff); - - /* Enable SPI interface */ - regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; - if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) - regval |= SPMODE_OP; - - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval); - spin_lock_init(&mpc8xxx_spi->lock); - init_completion(&mpc8xxx_spi->done); - INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work); - INIT_LIST_HEAD(&mpc8xxx_spi->queue); - - mpc8xxx_spi->workqueue = create_singlethread_workqueue( - dev_name(master->dev.parent)); - if (mpc8xxx_spi->workqueue == NULL) { - ret = -EBUSY; - goto free_irq; - } - - ret = spi_register_master(master); - if (ret < 0) - goto unreg_master; - - dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base, - mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags)); - - return master; - -unreg_master: - destroy_workqueue(mpc8xxx_spi->workqueue); -free_irq: - free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); -unmap_io: - iounmap(mpc8xxx_spi->base); -err_ioremap: - mpc8xxx_spi_cpm_free(mpc8xxx_spi); -err_cpm_init: - spi_master_put(master); -err: - return ERR_PTR(ret); -} - -static int __devexit mpc8xxx_spi_remove(struct device *dev) -{ - struct mpc8xxx_spi *mpc8xxx_spi; - struct spi_master *master; - - master = dev_get_drvdata(dev); - mpc8xxx_spi = spi_master_get_devdata(master); - - flush_workqueue(mpc8xxx_spi->workqueue); - destroy_workqueue(mpc8xxx_spi->workqueue); - spi_unregister_master(master); - - free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); - iounmap(mpc8xxx_spi->base); - mpc8xxx_spi_cpm_free(mpc8xxx_spi); - - return 0; -} - -struct mpc8xxx_spi_probe_info { - struct fsl_spi_platform_data pdata; - int *gpios; - bool *alow_flags; -}; - -static struct mpc8xxx_spi_probe_info * -to_of_pinfo(struct fsl_spi_platform_data *pdata) -{ - return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata); -} - -static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on) -{ - struct device *dev = spi->dev.parent; - struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data); - u16 cs = spi->chip_select; - int gpio = pinfo->gpios[cs]; - bool alow = pinfo->alow_flags[cs]; - - gpio_set_value(gpio, on ^ alow); -} - -static int of_mpc8xxx_spi_get_chipselects(struct device *dev) -{ - struct device_node *np = dev->of_node; - struct fsl_spi_platform_data *pdata = dev->platform_data; - struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); - unsigned int ngpios; - int i = 0; - int ret; - - ngpios = of_gpio_count(np); - if (!ngpios) { - /* - * SPI w/o chip-select line. One SPI device is still permitted - * though. - */ - pdata->max_chipselect = 1; - return 0; - } - - pinfo->gpios = kmalloc(ngpios * sizeof(*pinfo->gpios), GFP_KERNEL); - if (!pinfo->gpios) - return -ENOMEM; - memset(pinfo->gpios, -1, ngpios * sizeof(*pinfo->gpios)); - - pinfo->alow_flags = kzalloc(ngpios * sizeof(*pinfo->alow_flags), - GFP_KERNEL); - if (!pinfo->alow_flags) { - ret = -ENOMEM; - goto err_alloc_flags; - } - - for (; i < ngpios; i++) { - int gpio; - enum of_gpio_flags flags; - - gpio = of_get_gpio_flags(np, i, &flags); - if (!gpio_is_valid(gpio)) { - dev_err(dev, "invalid gpio #%d: %d\n", i, gpio); - ret = gpio; - goto err_loop; - } - - ret = gpio_request(gpio, dev_name(dev)); - if (ret) { - dev_err(dev, "can't request gpio #%d: %d\n", i, ret); - goto err_loop; - } - - pinfo->gpios[i] = gpio; - pinfo->alow_flags[i] = flags & OF_GPIO_ACTIVE_LOW; - - ret = gpio_direction_output(pinfo->gpios[i], - pinfo->alow_flags[i]); - if (ret) { - dev_err(dev, "can't set output direction for gpio " - "#%d: %d\n", i, ret); - goto err_loop; - } - } - - pdata->max_chipselect = ngpios; - pdata->cs_control = mpc8xxx_spi_cs_control; - - return 0; - -err_loop: - while (i >= 0) { - if (gpio_is_valid(pinfo->gpios[i])) - gpio_free(pinfo->gpios[i]); - i--; - } - - kfree(pinfo->alow_flags); - pinfo->alow_flags = NULL; -err_alloc_flags: - kfree(pinfo->gpios); - pinfo->gpios = NULL; - return ret; -} - -static int of_mpc8xxx_spi_free_chipselects(struct device *dev) -{ - struct fsl_spi_platform_data *pdata = dev->platform_data; - struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); - int i; - - if (!pinfo->gpios) - return 0; - - for (i = 0; i < pdata->max_chipselect; i++) { - if (gpio_is_valid(pinfo->gpios[i])) - gpio_free(pinfo->gpios[i]); - } - - kfree(pinfo->gpios); - kfree(pinfo->alow_flags); - return 0; -} - -static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev, - const struct of_device_id *ofid) -{ - struct device *dev = &ofdev->dev; - struct device_node *np = ofdev->dev.of_node; - struct mpc8xxx_spi_probe_info *pinfo; - struct fsl_spi_platform_data *pdata; - struct spi_master *master; - struct resource mem; - struct resource irq; - const void *prop; - int ret = -ENOMEM; - - pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); - if (!pinfo) - return -ENOMEM; - - pdata = &pinfo->pdata; - dev->platform_data = pdata; - - /* Allocate bus num dynamically. */ - pdata->bus_num = -1; - - /* SPI controller is either clocked from QE or SoC clock. */ - pdata->sysclk = get_brgfreq(); - if (pdata->sysclk == -1) { - pdata->sysclk = fsl_get_sys_freq(); - if (pdata->sysclk == -1) { - ret = -ENODEV; - goto err_clk; - } - } - - prop = of_get_property(np, "mode", NULL); - if (prop && !strcmp(prop, "cpu-qe")) - pdata->flags = SPI_QE_CPU_MODE; - else if (prop && !strcmp(prop, "qe")) - pdata->flags = SPI_CPM_MODE | SPI_QE; - else if (of_device_is_compatible(np, "fsl,cpm2-spi")) - pdata->flags = SPI_CPM_MODE | SPI_CPM2; - else if (of_device_is_compatible(np, "fsl,cpm1-spi")) - pdata->flags = SPI_CPM_MODE | SPI_CPM1; - - ret = of_mpc8xxx_spi_get_chipselects(dev); - if (ret) - goto err; - - ret = of_address_to_resource(np, 0, &mem); - if (ret) - goto err; - - ret = of_irq_to_resource(np, 0, &irq); - if (!ret) { - ret = -EINVAL; - goto err; - } - - master = mpc8xxx_spi_probe(dev, &mem, irq.start); - if (IS_ERR(master)) { - ret = PTR_ERR(master); - goto err; - } - - return 0; - -err: - of_mpc8xxx_spi_free_chipselects(dev); -err_clk: - kfree(pinfo); - return ret; -} - -static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev) -{ - int ret; - - ret = mpc8xxx_spi_remove(&ofdev->dev); - if (ret) - return ret; - of_mpc8xxx_spi_free_chipselects(&ofdev->dev); - return 0; -} - -static const struct of_device_id of_mpc8xxx_spi_match[] = { - { .compatible = "fsl,spi" }, - {}, -}; -MODULE_DEVICE_TABLE(of, of_mpc8xxx_spi_match); - -static struct of_platform_driver of_mpc8xxx_spi_driver = { - .driver = { - .name = "mpc8xxx_spi", - .owner = THIS_MODULE, - .of_match_table = of_mpc8xxx_spi_match, - }, - .probe = of_mpc8xxx_spi_probe, - .remove = __devexit_p(of_mpc8xxx_spi_remove), -}; - -#ifdef CONFIG_MPC832x_RDB -/* - * XXX XXX XXX - * This is "legacy" platform driver, was used by the MPC8323E-RDB boards - * only. The driver should go away soon, since newer MPC8323E-RDB's device - * tree can work with OpenFirmware driver. But for now we support old trees - * as well. - */ -static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev) -{ - struct resource *mem; - int irq; - struct spi_master *master; - - if (!pdev->dev.platform_data) - return -EINVAL; - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) - return -EINVAL; - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -EINVAL; - - master = mpc8xxx_spi_probe(&pdev->dev, mem, irq); - if (IS_ERR(master)) - return PTR_ERR(master); - return 0; -} - -static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev) -{ - return mpc8xxx_spi_remove(&pdev->dev); -} - -MODULE_ALIAS("platform:mpc8xxx_spi"); -static struct platform_driver mpc8xxx_spi_driver = { - .probe = plat_mpc8xxx_spi_probe, - .remove = __devexit_p(plat_mpc8xxx_spi_remove), - .driver = { - .name = "mpc8xxx_spi", - .owner = THIS_MODULE, - }, -}; - -static bool legacy_driver_failed; - -static void __init legacy_driver_register(void) -{ - legacy_driver_failed = platform_driver_register(&mpc8xxx_spi_driver); -} - -static void __exit legacy_driver_unregister(void) -{ - if (legacy_driver_failed) - return; - platform_driver_unregister(&mpc8xxx_spi_driver); -} -#else -static void __init legacy_driver_register(void) {} -static void __exit legacy_driver_unregister(void) {} -#endif /* CONFIG_MPC832x_RDB */ - -static int __init mpc8xxx_spi_init(void) -{ - legacy_driver_register(); - return of_register_platform_driver(&of_mpc8xxx_spi_driver); -} - -static void __exit mpc8xxx_spi_exit(void) -{ - of_unregister_platform_driver(&of_mpc8xxx_spi_driver); - legacy_driver_unregister(); -} - -module_init(mpc8xxx_spi_init); -module_exit(mpc8xxx_spi_exit); - -MODULE_AUTHOR("Kumar Gala"); -MODULE_DESCRIPTION("Simple MPC8xxx SPI Driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.2 From b36ece832512c1a0afa54ff0a56d63492a1caf08 Mon Sep 17 00:00:00 2001 From: Mingkai Hu Date: Tue, 12 Oct 2010 18:18:31 +0800 Subject: spi/mpc8xxx: refactor the common code for SPI/eSPI controller Refactor the common code in file spi_fsl_spi.c to spi_fsl_lib.c used by SPI/eSPI controller driver as a library, and leave the QE/CPM SPI controller code in the SPI controller driver spi_fsl_spi.c. Because the register map of the SPI controller and eSPI controller is so different, also leave the code operated the register to the driver code, not the common code. Signed-off-by: Mingkai Hu Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 5 + drivers/spi/Makefile | 1 + drivers/spi/spi_fsl_lib.c | 237 ++++++++++++++++++++ drivers/spi/spi_fsl_lib.h | 119 ++++++++++ drivers/spi/spi_fsl_spi.c | 552 ++++++++++++++-------------------------------- 5 files changed, 522 insertions(+), 392 deletions(-) create mode 100644 drivers/spi/spi_fsl_lib.c create mode 100644 drivers/spi/spi_fsl_lib.h (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index e8f02a6f2d23..0e6619426ea4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -182,9 +182,14 @@ config SPI_MPC512x_PSC This enables using the Freescale MPC5121 Programmable Serial Controller in SPI master mode. +config SPI_FSL_LIB + tristate + depends on FSL_SOC + config SPI_FSL_SPI tristate "Freescale SPI controller" depends on FSL_SOC + select SPI_FSL_LIB help This enables using the Freescale SPI controllers in master mode. MPC83xx platform uses the controller in cpu mode or CPM/QE mode. diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c6a56c798823..54bd95d63a59 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_SPI_PL022) += amba-pl022.o obj-$(CONFIG_SPI_MPC512x_PSC) += mpc512x_psc_spi.o obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o +obj-$(CONFIG_SPI_FSL_LIB) += spi_fsl_lib.o obj-$(CONFIG_SPI_FSL_SPI) += spi_fsl_spi.o obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi_fsl_lib.c new file mode 100644 index 000000000000..5cd741fdb5c3 --- /dev/null +++ b/drivers/spi/spi_fsl_lib.c @@ -0,0 +1,237 @@ +/* + * Freescale SPI/eSPI controller driver library. + * + * Maintainer: Kumar Gala + * + * Copyright (C) 2006 Polycom, Inc. + * + * CPM SPI and QE buffer descriptors mode support: + * Copyright (c) 2009 MontaVista Software, Inc. + * Author: Anton Vorontsov + * + * Copyright 2010 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi_fsl_lib.h" + +#define MPC8XXX_SPI_RX_BUF(type) \ +void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \ +{ \ + type *rx = mpc8xxx_spi->rx; \ + *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \ + mpc8xxx_spi->rx = rx; \ +} + +#define MPC8XXX_SPI_TX_BUF(type) \ +u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \ +{ \ + u32 data; \ + const type *tx = mpc8xxx_spi->tx; \ + if (!tx) \ + return 0; \ + data = *tx++ << mpc8xxx_spi->tx_shift; \ + mpc8xxx_spi->tx = tx; \ + return data; \ +} + +MPC8XXX_SPI_RX_BUF(u8) +MPC8XXX_SPI_RX_BUF(u16) +MPC8XXX_SPI_RX_BUF(u32) +MPC8XXX_SPI_TX_BUF(u8) +MPC8XXX_SPI_TX_BUF(u16) +MPC8XXX_SPI_TX_BUF(u32) + +struct mpc8xxx_spi_probe_info *to_of_pinfo(struct fsl_spi_platform_data *pdata) +{ + return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata); +} + +void mpc8xxx_spi_work(struct work_struct *work) +{ + struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi, + work); + + spin_lock_irq(&mpc8xxx_spi->lock); + while (!list_empty(&mpc8xxx_spi->queue)) { + struct spi_message *m = container_of(mpc8xxx_spi->queue.next, + struct spi_message, queue); + + list_del_init(&m->queue); + spin_unlock_irq(&mpc8xxx_spi->lock); + + if (mpc8xxx_spi->spi_do_one_msg) + mpc8xxx_spi->spi_do_one_msg(m); + + spin_lock_irq(&mpc8xxx_spi->lock); + } + spin_unlock_irq(&mpc8xxx_spi->lock); +} + +int mpc8xxx_spi_transfer(struct spi_device *spi, + struct spi_message *m) +{ + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + unsigned long flags; + + m->actual_length = 0; + m->status = -EINPROGRESS; + + spin_lock_irqsave(&mpc8xxx_spi->lock, flags); + list_add_tail(&m->queue, &mpc8xxx_spi->queue); + queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work); + spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags); + + return 0; +} + +void mpc8xxx_spi_cleanup(struct spi_device *spi) +{ + kfree(spi->controller_state); +} + +const char *mpc8xxx_spi_strmode(unsigned int flags) +{ + if (flags & SPI_QE_CPU_MODE) { + return "QE CPU"; + } else if (flags & SPI_CPM_MODE) { + if (flags & SPI_QE) + return "QE"; + else if (flags & SPI_CPM2) + return "CPM2"; + else + return "CPM1"; + } + return "CPU"; +} + +int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, + unsigned int irq) +{ + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct spi_master *master; + struct mpc8xxx_spi *mpc8xxx_spi; + int ret = 0; + + master = dev_get_drvdata(dev); + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH + | SPI_LSB_FIRST | SPI_LOOP; + + master->transfer = mpc8xxx_spi_transfer; + master->cleanup = mpc8xxx_spi_cleanup; + master->dev.of_node = dev->of_node; + + mpc8xxx_spi = spi_master_get_devdata(master); + mpc8xxx_spi->dev = dev; + mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8; + mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8; + mpc8xxx_spi->flags = pdata->flags; + mpc8xxx_spi->spibrg = pdata->sysclk; + mpc8xxx_spi->irq = irq; + + mpc8xxx_spi->rx_shift = 0; + mpc8xxx_spi->tx_shift = 0; + + init_completion(&mpc8xxx_spi->done); + + master->bus_num = pdata->bus_num; + master->num_chipselect = pdata->max_chipselect; + + spin_lock_init(&mpc8xxx_spi->lock); + init_completion(&mpc8xxx_spi->done); + INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work); + INIT_LIST_HEAD(&mpc8xxx_spi->queue); + + mpc8xxx_spi->workqueue = create_singlethread_workqueue( + dev_name(master->dev.parent)); + if (mpc8xxx_spi->workqueue == NULL) { + ret = -EBUSY; + goto err; + } + + return 0; + +err: + return ret; +} + +int __devexit mpc8xxx_spi_remove(struct device *dev) +{ + struct mpc8xxx_spi *mpc8xxx_spi; + struct spi_master *master; + + master = dev_get_drvdata(dev); + mpc8xxx_spi = spi_master_get_devdata(master); + + flush_workqueue(mpc8xxx_spi->workqueue); + destroy_workqueue(mpc8xxx_spi->workqueue); + spi_unregister_master(master); + + free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); + + if (mpc8xxx_spi->spi_remove) + mpc8xxx_spi->spi_remove(mpc8xxx_spi); + + return 0; +} + +int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev, + const struct of_device_id *ofid) +{ + struct device *dev = &ofdev->dev; + struct device_node *np = ofdev->dev.of_node; + struct mpc8xxx_spi_probe_info *pinfo; + struct fsl_spi_platform_data *pdata; + const void *prop; + int ret = -ENOMEM; + + pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); + if (!pinfo) + return -ENOMEM; + + pdata = &pinfo->pdata; + dev->platform_data = pdata; + + /* Allocate bus num dynamically. */ + pdata->bus_num = -1; + + /* SPI controller is either clocked from QE or SoC clock. */ + pdata->sysclk = get_brgfreq(); + if (pdata->sysclk == -1) { + pdata->sysclk = fsl_get_sys_freq(); + if (pdata->sysclk == -1) { + ret = -ENODEV; + goto err; + } + } + + prop = of_get_property(np, "mode", NULL); + if (prop && !strcmp(prop, "cpu-qe")) + pdata->flags = SPI_QE_CPU_MODE; + else if (prop && !strcmp(prop, "qe")) + pdata->flags = SPI_CPM_MODE | SPI_QE; + else if (of_device_is_compatible(np, "fsl,cpm2-spi")) + pdata->flags = SPI_CPM_MODE | SPI_CPM2; + else if (of_device_is_compatible(np, "fsl,cpm1-spi")) + pdata->flags = SPI_CPM_MODE | SPI_CPM1; + + return 0; + +err: + kfree(pinfo); + return ret; +} diff --git a/drivers/spi/spi_fsl_lib.h b/drivers/spi/spi_fsl_lib.h new file mode 100644 index 000000000000..6ae894917387 --- /dev/null +++ b/drivers/spi/spi_fsl_lib.h @@ -0,0 +1,119 @@ +/* + * Freescale SPI/eSPI controller driver library. + * + * Maintainer: Kumar Gala + * + * Copyright 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2006 Polycom, Inc. + * + * CPM SPI and QE buffer descriptors mode support: + * Copyright (c) 2009 MontaVista Software, Inc. + * Author: Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef __SPI_FSL_LIB_H__ +#define __SPI_FSL_LIB_H__ + +/* SPI/eSPI Controller driver's private data. */ +struct mpc8xxx_spi { + struct device *dev; + void *reg_base; + + /* rx & tx bufs from the spi_transfer */ + const void *tx; + void *rx; + + int subblock; + struct spi_pram __iomem *pram; + struct cpm_buf_desc __iomem *tx_bd; + struct cpm_buf_desc __iomem *rx_bd; + + struct spi_transfer *xfer_in_progress; + + /* dma addresses for CPM transfers */ + dma_addr_t tx_dma; + dma_addr_t rx_dma; + bool map_tx_dma; + bool map_rx_dma; + + dma_addr_t dma_dummy_tx; + dma_addr_t dma_dummy_rx; + + /* functions to deal with different sized buffers */ + void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *); + u32(*get_tx) (struct mpc8xxx_spi *); + + /* hooks for different controller driver */ + void (*spi_do_one_msg) (struct spi_message *m); + void (*spi_remove) (struct mpc8xxx_spi *mspi); + + unsigned int count; + unsigned int irq; + + unsigned nsecs; /* (clock cycle time)/2 */ + + u32 spibrg; /* SPIBRG input clock */ + u32 rx_shift; /* RX data reg shift when in qe mode */ + u32 tx_shift; /* TX data reg shift when in qe mode */ + + unsigned int flags; + + struct workqueue_struct *workqueue; + struct work_struct work; + + struct list_head queue; + spinlock_t lock; + + struct completion done; +}; + +struct spi_mpc8xxx_cs { + /* functions to deal with different sized buffers */ + void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *); + u32 (*get_tx) (struct mpc8xxx_spi *); + u32 rx_shift; /* RX data reg shift when in qe mode */ + u32 tx_shift; /* TX data reg shift when in qe mode */ + u32 hw_mode; /* Holds HW mode register settings */ +}; + +static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val) +{ + out_be32(reg, val); +} + +static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg) +{ + return in_be32(reg); +} + +struct mpc8xxx_spi_probe_info { + struct fsl_spi_platform_data pdata; + int *gpios; + bool *alow_flags; +}; + +extern u32 mpc8xxx_spi_tx_buf_u8(struct mpc8xxx_spi *mpc8xxx_spi); +extern u32 mpc8xxx_spi_tx_buf_u16(struct mpc8xxx_spi *mpc8xxx_spi); +extern u32 mpc8xxx_spi_tx_buf_u32(struct mpc8xxx_spi *mpc8xxx_spi); +extern void mpc8xxx_spi_rx_buf_u8(u32 data, struct mpc8xxx_spi *mpc8xxx_spi); +extern void mpc8xxx_spi_rx_buf_u16(u32 data, struct mpc8xxx_spi *mpc8xxx_spi); +extern void mpc8xxx_spi_rx_buf_u32(u32 data, struct mpc8xxx_spi *mpc8xxx_spi); + +extern struct mpc8xxx_spi_probe_info *to_of_pinfo( + struct fsl_spi_platform_data *pdata); +extern int mpc8xxx_spi_bufs(struct mpc8xxx_spi *mspi, + struct spi_transfer *t, unsigned int len); +extern int mpc8xxx_spi_transfer(struct spi_device *spi, struct spi_message *m); +extern void mpc8xxx_spi_cleanup(struct spi_device *spi); +extern const char *mpc8xxx_spi_strmode(unsigned int flags); +extern int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, + unsigned int irq); +extern int mpc8xxx_spi_remove(struct device *dev); +extern int of_mpc8xxx_spi_probe(struct platform_device *ofdev, + const struct of_device_id *ofid); + +#endif /* __SPI_FSL_LIB_H__ */ diff --git a/drivers/spi/spi_fsl_spi.c b/drivers/spi/spi_fsl_spi.c index 1dd86b835cd8..7ca52d3ae8f8 100644 --- a/drivers/spi/spi_fsl_spi.c +++ b/drivers/spi/spi_fsl_spi.c @@ -1,9 +1,10 @@ /* - * MPC8xxx SPI controller driver. + * Freescale SPI controller driver. * * Maintainer: Kumar Gala * * Copyright (C) 2006 Polycom, Inc. + * Copyright 2010 Freescale Semiconductor, Inc. * * CPM SPI and QE buffer descriptors mode support: * Copyright (c) 2009 MontaVista Software, Inc. @@ -15,18 +16,11 @@ * option) any later version. */ #include -#include #include #include -#include -#include -#include -#include -#include #include #include #include -#include #include #include #include @@ -38,12 +32,12 @@ #include #include #include -#include #include #include #include -#include + +#include "spi_fsl_lib.h" /* CPM1 and CPM2 are mutually exclusive. */ #ifdef CONFIG_CPM1 @@ -55,7 +49,7 @@ #endif /* SPI Controller registers */ -struct mpc8xxx_spi_reg { +struct fsl_spi_reg { u8 res1[0x20]; __be32 mode; __be32 event; @@ -80,7 +74,7 @@ struct mpc8xxx_spi_reg { /* * Default for SPI Mode: - * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk + * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk */ #define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \ SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf)) @@ -102,112 +96,16 @@ struct mpc8xxx_spi_reg { #define SPI_PRAM_SIZE 0x100 #define SPI_MRBLR ((unsigned int)PAGE_SIZE) -/* SPI Controller driver's private data. */ -struct mpc8xxx_spi { - struct device *dev; - struct mpc8xxx_spi_reg __iomem *base; - - /* rx & tx bufs from the spi_transfer */ - const void *tx; - void *rx; - - int subblock; - struct spi_pram __iomem *pram; - struct cpm_buf_desc __iomem *tx_bd; - struct cpm_buf_desc __iomem *rx_bd; - - struct spi_transfer *xfer_in_progress; - - /* dma addresses for CPM transfers */ - dma_addr_t tx_dma; - dma_addr_t rx_dma; - bool map_tx_dma; - bool map_rx_dma; - - dma_addr_t dma_dummy_tx; - dma_addr_t dma_dummy_rx; - - /* functions to deal with different sized buffers */ - void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *); - u32(*get_tx) (struct mpc8xxx_spi *); - - unsigned int count; - unsigned int irq; - - unsigned nsecs; /* (clock cycle time)/2 */ - - u32 spibrg; /* SPIBRG input clock */ - u32 rx_shift; /* RX data reg shift when in qe mode */ - u32 tx_shift; /* TX data reg shift when in qe mode */ - - unsigned int flags; - - struct workqueue_struct *workqueue; - struct work_struct work; - - struct list_head queue; - spinlock_t lock; - - struct completion done; -}; - -static void *mpc8xxx_dummy_rx; -static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock); -static int mpc8xxx_dummy_rx_refcnt; - -struct spi_mpc8xxx_cs { - /* functions to deal with different sized buffers */ - void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *); - u32 (*get_tx) (struct mpc8xxx_spi *); - u32 rx_shift; /* RX data reg shift when in qe mode */ - u32 tx_shift; /* TX data reg shift when in qe mode */ - u32 hw_mode; /* Holds HW mode register settings */ -}; - -static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val) -{ - out_be32(reg, val); -} - -static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg) -{ - return in_be32(reg); -} - -#define MPC83XX_SPI_RX_BUF(type) \ -static \ -void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \ -{ \ - type *rx = mpc8xxx_spi->rx; \ - *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \ - mpc8xxx_spi->rx = rx; \ -} - -#define MPC83XX_SPI_TX_BUF(type) \ -static \ -u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \ -{ \ - u32 data; \ - const type *tx = mpc8xxx_spi->tx; \ - if (!tx) \ - return 0; \ - data = *tx++ << mpc8xxx_spi->tx_shift; \ - mpc8xxx_spi->tx = tx; \ - return data; \ -} +static void *fsl_dummy_rx; +static DEFINE_MUTEX(fsl_dummy_rx_lock); +static int fsl_dummy_rx_refcnt; -MPC83XX_SPI_RX_BUF(u8) -MPC83XX_SPI_RX_BUF(u16) -MPC83XX_SPI_RX_BUF(u32) -MPC83XX_SPI_TX_BUF(u8) -MPC83XX_SPI_TX_BUF(u16) -MPC83XX_SPI_TX_BUF(u32) - -static void mpc8xxx_spi_change_mode(struct spi_device *spi) +static void fsl_spi_change_mode(struct spi_device *spi) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); struct spi_mpc8xxx_cs *cs = spi->controller_state; - __be32 __iomem *mode = &mspi->base->mode; + struct fsl_spi_reg *reg_base = mspi->reg_base; + __be32 __iomem *mode = ®_base->mode; unsigned long flags; if (cs->hw_mode == mpc8xxx_spi_read_reg(mode)) @@ -238,7 +136,7 @@ static void mpc8xxx_spi_change_mode(struct spi_device *spi) local_irq_restore(flags); } -static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value) +static void fsl_spi_chipselect(struct spi_device *spi, int value) { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data; @@ -256,18 +154,17 @@ static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value) mpc8xxx_spi->get_rx = cs->get_rx; mpc8xxx_spi->get_tx = cs->get_tx; - mpc8xxx_spi_change_mode(spi); + fsl_spi_change_mode(spi); if (pdata->cs_control) pdata->cs_control(spi, pol); } } -static int -mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs, - struct spi_device *spi, - struct mpc8xxx_spi *mpc8xxx_spi, - int bits_per_word) +static int mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs, + struct spi_device *spi, + struct mpc8xxx_spi *mpc8xxx_spi, + int bits_per_word) { cs->rx_shift = 0; cs->tx_shift = 0; @@ -307,10 +204,9 @@ mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs, return bits_per_word; } -static int -mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs, - struct spi_device *spi, - int bits_per_word) +static int mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs, + struct spi_device *spi, + int bits_per_word) { /* QE uses Little Endian for words > 8 * so transform all words > 8 into 8 bits @@ -326,13 +222,13 @@ mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs, return bits_per_word; } -static -int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) +static int fsl_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) { struct mpc8xxx_spi *mpc8xxx_spi; - int bits_per_word; + int bits_per_word = 0; u8 pm; - u32 hz; + u32 hz = 0; struct spi_mpc8xxx_cs *cs = spi->controller_state; mpc8xxx_spi = spi_master_get_devdata(spi->master); @@ -340,9 +236,6 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) if (t) { bits_per_word = t->bits_per_word; hz = t->speed_hz; - } else { - bits_per_word = 0; - hz = 0; } /* spi_transfer level calls that work per-word */ @@ -388,23 +281,25 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) hz, mpc8xxx_spi->spibrg / 1024); if (pm > 16) pm = 16; - } else + } else { pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1; + } if (pm) pm--; cs->hw_mode |= SPMODE_PM(pm); - mpc8xxx_spi_change_mode(spi); + fsl_spi_change_mode(spi); return 0; } -static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi) +static void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi) { struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd; struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd; unsigned int xfer_len = min(mspi->count, SPI_MRBLR); unsigned int xfer_ofs; + struct fsl_spi_reg *reg_base = mspi->reg_base; xfer_ofs = mspi->xfer_in_progress->len - mspi->count; @@ -424,13 +319,14 @@ static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi) BD_SC_LAST); /* start transfer */ - mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR); + mpc8xxx_spi_write_reg(®_base->command, SPCOM_STR); } -static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi, +static int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, bool is_dma_mapped) { struct device *dev = mspi->dev; + struct fsl_spi_reg *reg_base = mspi->reg_base; if (is_dma_mapped) { mspi->map_tx_dma = 0; @@ -475,13 +371,13 @@ static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi, } /* enable rx ints */ - mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB); + mpc8xxx_spi_write_reg(®_base->mask, SPIE_RXB); mspi->xfer_in_progress = t; mspi->count = t->len; /* start CPM transfers */ - mpc8xxx_spi_cpm_bufs_start(mspi); + fsl_spi_cpm_bufs_start(mspi); return 0; @@ -491,7 +387,7 @@ err_rx_dma: return -ENOMEM; } -static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) +static void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) { struct device *dev = mspi->dev; struct spi_transfer *t = mspi->xfer_in_progress; @@ -503,31 +399,34 @@ static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) mspi->xfer_in_progress = NULL; } -static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi, +static int fsl_spi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, unsigned int len) { u32 word; + struct fsl_spi_reg *reg_base = mspi->reg_base; mspi->count = len; /* enable rx ints */ - mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE); + mpc8xxx_spi_write_reg(®_base->mask, SPIM_NE); /* transmit word */ word = mspi->get_tx(mspi); - mpc8xxx_spi_write_reg(&mspi->base->transmit, word); + mpc8xxx_spi_write_reg(®_base->transmit, word); return 0; } -static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t, +static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t, bool is_dma_mapped) { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + struct fsl_spi_reg *reg_base; unsigned int len = t->len; u8 bits_per_word; int ret; + reg_base = mpc8xxx_spi->reg_base; bits_per_word = spi->bits_per_word; if (t->bits_per_word) bits_per_word = t->bits_per_word; @@ -551,24 +450,24 @@ static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t, INIT_COMPLETION(mpc8xxx_spi->done); if (mpc8xxx_spi->flags & SPI_CPM_MODE) - ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped); + ret = fsl_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped); else - ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len); + ret = fsl_spi_cpu_bufs(mpc8xxx_spi, t, len); if (ret) return ret; wait_for_completion(&mpc8xxx_spi->done); /* disable rx ints */ - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0); + mpc8xxx_spi_write_reg(®_base->mask, 0); if (mpc8xxx_spi->flags & SPI_CPM_MODE) - mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi); + fsl_spi_cpm_bufs_complete(mpc8xxx_spi); return mpc8xxx_spi->count; } -static void mpc8xxx_spi_do_one_msg(struct spi_message *m) +static void fsl_spi_do_one_msg(struct spi_message *m) { struct spi_device *spi = m->spi; struct spi_transfer *t; @@ -584,18 +483,18 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m) status = -EINVAL; if (cs_change) - status = mpc8xxx_spi_setup_transfer(spi, t); + status = fsl_spi_setup_transfer(spi, t); if (status < 0) break; } if (cs_change) { - mpc8xxx_spi_chipselect(spi, BITBANG_CS_ACTIVE); + fsl_spi_chipselect(spi, BITBANG_CS_ACTIVE); ndelay(nsecs); } cs_change = t->cs_change; if (t->len) - status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped); + status = fsl_spi_bufs(spi, t, m->is_dma_mapped); if (status) { status = -EMSGSIZE; break; @@ -607,7 +506,7 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m) if (cs_change) { ndelay(nsecs); - mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE); + fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE); ndelay(nsecs); } } @@ -617,35 +516,16 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m) if (status || !cs_change) { ndelay(nsecs); - mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE); + fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE); } - mpc8xxx_spi_setup_transfer(spi, NULL); -} - -static void mpc8xxx_spi_work(struct work_struct *work) -{ - struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi, - work); - - spin_lock_irq(&mpc8xxx_spi->lock); - while (!list_empty(&mpc8xxx_spi->queue)) { - struct spi_message *m = container_of(mpc8xxx_spi->queue.next, - struct spi_message, queue); - - list_del_init(&m->queue); - spin_unlock_irq(&mpc8xxx_spi->lock); - - mpc8xxx_spi_do_one_msg(m); - - spin_lock_irq(&mpc8xxx_spi->lock); - } - spin_unlock_irq(&mpc8xxx_spi->lock); + fsl_spi_setup_transfer(spi, NULL); } -static int mpc8xxx_spi_setup(struct spi_device *spi) +static int fsl_spi_setup(struct spi_device *spi) { struct mpc8xxx_spi *mpc8xxx_spi; + struct fsl_spi_reg *reg_base; int retval; u32 hw_mode; struct spi_mpc8xxx_cs *cs = spi->controller_state; @@ -661,8 +541,10 @@ static int mpc8xxx_spi_setup(struct spi_device *spi) } mpc8xxx_spi = spi_master_get_devdata(spi->master); + reg_base = mpc8xxx_spi->reg_base; + hw_mode = cs->hw_mode; /* Save original settings */ - cs->hw_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode); + cs->hw_mode = mpc8xxx_spi_read_reg(®_base->mode); /* mask out bits we are going to set */ cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH | SPMODE_REV | SPMODE_LOOP); @@ -676,7 +558,7 @@ static int mpc8xxx_spi_setup(struct spi_device *spi) if (spi->mode & SPI_LOOP) cs->hw_mode |= SPMODE_LOOP; - retval = mpc8xxx_spi_setup_transfer(spi, NULL); + retval = fsl_spi_setup_transfer(spi, NULL); if (retval < 0) { cs->hw_mode = hw_mode; /* Restore settings */ return retval; @@ -684,9 +566,10 @@ static int mpc8xxx_spi_setup(struct spi_device *spi) return 0; } -static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) +static void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) { u16 len; + struct fsl_spi_reg *reg_base = mspi->reg_base; dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__, in_be16(&mspi->rx_bd->cbd_datlen), mspi->count); @@ -698,20 +581,22 @@ static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) } /* Clear the events */ - mpc8xxx_spi_write_reg(&mspi->base->event, events); + mpc8xxx_spi_write_reg(®_base->event, events); mspi->count -= len; if (mspi->count) - mpc8xxx_spi_cpm_bufs_start(mspi); + fsl_spi_cpm_bufs_start(mspi); else complete(&mspi->done); } -static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) +static void fsl_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) { + struct fsl_spi_reg *reg_base = mspi->reg_base; + /* We need handle RX first */ if (events & SPIE_NE) { - u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive); + u32 rx_data = mpc8xxx_spi_read_reg(®_base->receive); if (mspi->rx) mspi->get_rx(rx_data, mspi); @@ -720,102 +605,80 @@ static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) if ((events & SPIE_NF) == 0) /* spin until TX is done */ while (((events = - mpc8xxx_spi_read_reg(&mspi->base->event)) & + mpc8xxx_spi_read_reg(®_base->event)) & SPIE_NF) == 0) cpu_relax(); /* Clear the events */ - mpc8xxx_spi_write_reg(&mspi->base->event, events); + mpc8xxx_spi_write_reg(®_base->event, events); mspi->count -= 1; if (mspi->count) { u32 word = mspi->get_tx(mspi); - mpc8xxx_spi_write_reg(&mspi->base->transmit, word); + mpc8xxx_spi_write_reg(®_base->transmit, word); } else { complete(&mspi->done); } } -static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data) +static irqreturn_t fsl_spi_irq(s32 irq, void *context_data) { struct mpc8xxx_spi *mspi = context_data; irqreturn_t ret = IRQ_NONE; u32 events; + struct fsl_spi_reg *reg_base = mspi->reg_base; /* Get interrupt events(tx/rx) */ - events = mpc8xxx_spi_read_reg(&mspi->base->event); + events = mpc8xxx_spi_read_reg(®_base->event); if (events) ret = IRQ_HANDLED; dev_dbg(mspi->dev, "%s: events %x\n", __func__, events); if (mspi->flags & SPI_CPM_MODE) - mpc8xxx_spi_cpm_irq(mspi, events); + fsl_spi_cpm_irq(mspi, events); else - mpc8xxx_spi_cpu_irq(mspi, events); + fsl_spi_cpu_irq(mspi, events); return ret; } -static int mpc8xxx_spi_transfer(struct spi_device *spi, - struct spi_message *m) +static void *fsl_spi_alloc_dummy_rx(void) { - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); - unsigned long flags; + mutex_lock(&fsl_dummy_rx_lock); - m->actual_length = 0; - m->status = -EINPROGRESS; + if (!fsl_dummy_rx) + fsl_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL); + if (fsl_dummy_rx) + fsl_dummy_rx_refcnt++; - spin_lock_irqsave(&mpc8xxx_spi->lock, flags); - list_add_tail(&m->queue, &mpc8xxx_spi->queue); - queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work); - spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags); + mutex_unlock(&fsl_dummy_rx_lock); - return 0; + return fsl_dummy_rx; } - -static void mpc8xxx_spi_cleanup(struct spi_device *spi) +static void fsl_spi_free_dummy_rx(void) { - kfree(spi->controller_state); -} + mutex_lock(&fsl_dummy_rx_lock); -static void *mpc8xxx_spi_alloc_dummy_rx(void) -{ - mutex_lock(&mpc8xxx_dummy_rx_lock); - - if (!mpc8xxx_dummy_rx) - mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL); - if (mpc8xxx_dummy_rx) - mpc8xxx_dummy_rx_refcnt++; - - mutex_unlock(&mpc8xxx_dummy_rx_lock); - - return mpc8xxx_dummy_rx; -} - -static void mpc8xxx_spi_free_dummy_rx(void) -{ - mutex_lock(&mpc8xxx_dummy_rx_lock); - - switch (mpc8xxx_dummy_rx_refcnt) { + switch (fsl_dummy_rx_refcnt) { case 0: WARN_ON(1); break; case 1: - kfree(mpc8xxx_dummy_rx); - mpc8xxx_dummy_rx = NULL; + kfree(fsl_dummy_rx); + fsl_dummy_rx = NULL; /* fall through */ default: - mpc8xxx_dummy_rx_refcnt--; + fsl_dummy_rx_refcnt--; break; } - mutex_unlock(&mpc8xxx_dummy_rx_lock); + mutex_unlock(&fsl_dummy_rx_lock); } -static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi) +static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi) { struct device *dev = mspi->dev; struct device_node *np = dev->of_node; @@ -869,7 +732,7 @@ static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi) return pram_ofs; } -static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi) +static int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi) { struct device *dev = mspi->dev; struct device_node *np = dev->of_node; @@ -881,7 +744,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi) if (!(mspi->flags & SPI_CPM_MODE)) return 0; - if (!mpc8xxx_spi_alloc_dummy_rx()) + if (!fsl_spi_alloc_dummy_rx()) return -ENOMEM; if (mspi->flags & SPI_QE) { @@ -902,7 +765,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi) } } - pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi); + pram_ofs = fsl_spi_cpm_get_pram(mspi); if (IS_ERR_VALUE(pram_ofs)) { dev_err(dev, "can't allocate spi parameter ram\n"); goto err_pram; @@ -922,7 +785,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi) goto err_dummy_tx; } - mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR, + mspi->dma_dummy_rx = dma_map_single(dev, fsl_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE); if (dma_mapping_error(dev, mspi->dma_dummy_rx)) { dev_err(dev, "unable to map dummy rx buffer\n"); @@ -960,11 +823,11 @@ err_dummy_tx: err_bds: cpm_muram_free(pram_ofs); err_pram: - mpc8xxx_spi_free_dummy_rx(); + fsl_spi_free_dummy_rx(); return -ENOMEM; } -static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi) +static void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi) { struct device *dev = mspi->dev; @@ -972,30 +835,22 @@ static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi) dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE); cpm_muram_free(cpm_muram_offset(mspi->tx_bd)); cpm_muram_free(cpm_muram_offset(mspi->pram)); - mpc8xxx_spi_free_dummy_rx(); + fsl_spi_free_dummy_rx(); } -static const char *mpc8xxx_spi_strmode(unsigned int flags) +static void fsl_spi_remove(struct mpc8xxx_spi *mspi) { - if (flags & SPI_QE_CPU_MODE) { - return "QE CPU"; - } else if (flags & SPI_CPM_MODE) { - if (flags & SPI_QE) - return "QE"; - else if (flags & SPI_CPM2) - return "CPM2"; - else - return "CPM1"; - } - return "CPU"; + iounmap(mspi->reg_base); + fsl_spi_cpm_free(mspi); } -static struct spi_master * __devinit -mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) +static struct spi_master * __devinit fsl_spi_probe(struct device *dev, + struct resource *mem, unsigned int irq) { struct fsl_spi_platform_data *pdata = dev->platform_data; struct spi_master *master; struct mpc8xxx_spi *mpc8xxx_spi; + struct fsl_spi_reg *reg_base; u32 regval; int ret = 0; @@ -1007,132 +862,77 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) dev_set_drvdata(dev, master); - /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH - | SPI_LSB_FIRST | SPI_LOOP; + ret = mpc8xxx_spi_probe(dev, mem, irq); + if (ret) + goto err_probe; - master->setup = mpc8xxx_spi_setup; - master->transfer = mpc8xxx_spi_transfer; - master->cleanup = mpc8xxx_spi_cleanup; - master->dev.of_node = dev->of_node; + master->setup = fsl_spi_setup; mpc8xxx_spi = spi_master_get_devdata(master); - mpc8xxx_spi->dev = dev; - mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8; - mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8; - mpc8xxx_spi->flags = pdata->flags; - mpc8xxx_spi->spibrg = pdata->sysclk; + mpc8xxx_spi->spi_do_one_msg = fsl_spi_do_one_msg; + mpc8xxx_spi->spi_remove = fsl_spi_remove; + - ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi); + ret = fsl_spi_cpm_init(mpc8xxx_spi); if (ret) goto err_cpm_init; - mpc8xxx_spi->rx_shift = 0; - mpc8xxx_spi->tx_shift = 0; if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { mpc8xxx_spi->rx_shift = 16; mpc8xxx_spi->tx_shift = 24; } - init_completion(&mpc8xxx_spi->done); - - mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem)); - if (mpc8xxx_spi->base == NULL) { + mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem)); + if (mpc8xxx_spi->reg_base == NULL) { ret = -ENOMEM; goto err_ioremap; } - mpc8xxx_spi->irq = irq; - /* Register for SPI Interrupt */ - ret = request_irq(mpc8xxx_spi->irq, mpc8xxx_spi_irq, - 0, "mpc8xxx_spi", mpc8xxx_spi); + ret = request_irq(mpc8xxx_spi->irq, fsl_spi_irq, + 0, "fsl_spi", mpc8xxx_spi); if (ret != 0) - goto unmap_io; + goto free_irq; - master->bus_num = pdata->bus_num; - master->num_chipselect = pdata->max_chipselect; + reg_base = mpc8xxx_spi->reg_base; /* SPI controller initializations */ - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, 0); - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0); - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->command, 0); - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, 0xffffffff); + mpc8xxx_spi_write_reg(®_base->mode, 0); + mpc8xxx_spi_write_reg(®_base->mask, 0); + mpc8xxx_spi_write_reg(®_base->command, 0); + mpc8xxx_spi_write_reg(®_base->event, 0xffffffff); /* Enable SPI interface */ regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) regval |= SPMODE_OP; - mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval); - spin_lock_init(&mpc8xxx_spi->lock); - init_completion(&mpc8xxx_spi->done); - INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work); - INIT_LIST_HEAD(&mpc8xxx_spi->queue); - - mpc8xxx_spi->workqueue = create_singlethread_workqueue( - dev_name(master->dev.parent)); - if (mpc8xxx_spi->workqueue == NULL) { - ret = -EBUSY; - goto free_irq; - } + mpc8xxx_spi_write_reg(®_base->mode, regval); ret = spi_register_master(master); if (ret < 0) goto unreg_master; - dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base, + dev_info(dev, "at 0x%p (irq = %d), %s mode\n", reg_base, mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags)); return master; unreg_master: - destroy_workqueue(mpc8xxx_spi->workqueue); -free_irq: free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); -unmap_io: - iounmap(mpc8xxx_spi->base); +free_irq: + iounmap(mpc8xxx_spi->reg_base); err_ioremap: - mpc8xxx_spi_cpm_free(mpc8xxx_spi); + fsl_spi_cpm_free(mpc8xxx_spi); err_cpm_init: +err_probe: spi_master_put(master); err: return ERR_PTR(ret); } -static int __devexit mpc8xxx_spi_remove(struct device *dev) -{ - struct mpc8xxx_spi *mpc8xxx_spi; - struct spi_master *master; - - master = dev_get_drvdata(dev); - mpc8xxx_spi = spi_master_get_devdata(master); - - flush_workqueue(mpc8xxx_spi->workqueue); - destroy_workqueue(mpc8xxx_spi->workqueue); - spi_unregister_master(master); - - free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); - iounmap(mpc8xxx_spi->base); - mpc8xxx_spi_cpm_free(mpc8xxx_spi); - - return 0; -} - -struct mpc8xxx_spi_probe_info { - struct fsl_spi_platform_data pdata; - int *gpios; - bool *alow_flags; -}; - -static struct mpc8xxx_spi_probe_info * -to_of_pinfo(struct fsl_spi_platform_data *pdata) -{ - return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata); -} - -static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on) +static void fsl_spi_cs_control(struct spi_device *spi, bool on) { struct device *dev = spi->dev.parent; struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data); @@ -1143,7 +943,7 @@ static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on) gpio_set_value(gpio, on ^ alow); } -static int of_mpc8xxx_spi_get_chipselects(struct device *dev) +static int of_fsl_spi_get_chipselects(struct device *dev) { struct device_node *np = dev->of_node; struct fsl_spi_platform_data *pdata = dev->platform_data; @@ -1204,7 +1004,7 @@ static int of_mpc8xxx_spi_get_chipselects(struct device *dev) } pdata->max_chipselect = ngpios; - pdata->cs_control = mpc8xxx_spi_cs_control; + pdata->cs_control = fsl_spi_cs_control; return 0; @@ -1223,7 +1023,7 @@ err_alloc_flags: return ret; } -static int of_mpc8xxx_spi_free_chipselects(struct device *dev) +static int of_fsl_spi_free_chipselects(struct device *dev) { struct fsl_spi_platform_data *pdata = dev->platform_data; struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); @@ -1242,50 +1042,21 @@ static int of_mpc8xxx_spi_free_chipselects(struct device *dev) return 0; } -static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev, - const struct of_device_id *ofid) +static int __devinit of_fsl_spi_probe(struct platform_device *ofdev, + const struct of_device_id *ofid) { struct device *dev = &ofdev->dev; struct device_node *np = ofdev->dev.of_node; - struct mpc8xxx_spi_probe_info *pinfo; - struct fsl_spi_platform_data *pdata; struct spi_master *master; struct resource mem; struct resource irq; - const void *prop; int ret = -ENOMEM; - pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); - if (!pinfo) - return -ENOMEM; - - pdata = &pinfo->pdata; - dev->platform_data = pdata; - - /* Allocate bus num dynamically. */ - pdata->bus_num = -1; - - /* SPI controller is either clocked from QE or SoC clock. */ - pdata->sysclk = get_brgfreq(); - if (pdata->sysclk == -1) { - pdata->sysclk = fsl_get_sys_freq(); - if (pdata->sysclk == -1) { - ret = -ENODEV; - goto err_clk; - } - } + ret = of_mpc8xxx_spi_probe(ofdev, ofid); + if (ret) + return ret; - prop = of_get_property(np, "mode", NULL); - if (prop && !strcmp(prop, "cpu-qe")) - pdata->flags = SPI_QE_CPU_MODE; - else if (prop && !strcmp(prop, "qe")) - pdata->flags = SPI_CPM_MODE | SPI_QE; - else if (of_device_is_compatible(np, "fsl,cpm2-spi")) - pdata->flags = SPI_CPM_MODE | SPI_CPM2; - else if (of_device_is_compatible(np, "fsl,cpm1-spi")) - pdata->flags = SPI_CPM_MODE | SPI_CPM1; - - ret = of_mpc8xxx_spi_get_chipselects(dev); + ret = of_fsl_spi_get_chipselects(dev); if (ret) goto err; @@ -1299,7 +1070,7 @@ static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev, goto err; } - master = mpc8xxx_spi_probe(dev, &mem, irq.start); + master = fsl_spi_probe(dev, &mem, irq.start); if (IS_ERR(master)) { ret = PTR_ERR(master); goto err; @@ -1308,42 +1079,40 @@ static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev, return 0; err: - of_mpc8xxx_spi_free_chipselects(dev); -err_clk: - kfree(pinfo); + of_fsl_spi_free_chipselects(dev); return ret; } -static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev) +static int __devexit of_fsl_spi_remove(struct platform_device *ofdev) { int ret; ret = mpc8xxx_spi_remove(&ofdev->dev); if (ret) return ret; - of_mpc8xxx_spi_free_chipselects(&ofdev->dev); + of_fsl_spi_free_chipselects(&ofdev->dev); return 0; } -static const struct of_device_id of_mpc8xxx_spi_match[] = { +static const struct of_device_id of_fsl_spi_match[] = { { .compatible = "fsl,spi" }, - {}, + {} }; -MODULE_DEVICE_TABLE(of, of_mpc8xxx_spi_match); +MODULE_DEVICE_TABLE(of, of_fsl_spi_match); -static struct of_platform_driver of_mpc8xxx_spi_driver = { +static struct of_platform_driver of_fsl_spi_driver = { .driver = { - .name = "mpc8xxx_spi", + .name = "fsl_spi", .owner = THIS_MODULE, - .of_match_table = of_mpc8xxx_spi_match, + .of_match_table = of_fsl_spi_match, }, - .probe = of_mpc8xxx_spi_probe, - .remove = __devexit_p(of_mpc8xxx_spi_remove), + .probe = of_fsl_spi_probe, + .remove = __devexit_p(of_fsl_spi_remove), }; #ifdef CONFIG_MPC832x_RDB /* - * XXX XXX XXX + * XXX XXX XXX * This is "legacy" platform driver, was used by the MPC8323E-RDB boards * only. The driver should go away soon, since newer MPC8323E-RDB's device * tree can work with OpenFirmware driver. But for now we support old trees @@ -1366,7 +1135,7 @@ static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev) if (irq <= 0) return -EINVAL; - master = mpc8xxx_spi_probe(&pdev->dev, mem, irq); + master = fsl_spi_probe(&pdev->dev, mem, irq); if (IS_ERR(master)) return PTR_ERR(master); return 0; @@ -1405,21 +1174,20 @@ static void __init legacy_driver_register(void) {} static void __exit legacy_driver_unregister(void) {} #endif /* CONFIG_MPC832x_RDB */ -static int __init mpc8xxx_spi_init(void) +static int __init fsl_spi_init(void) { legacy_driver_register(); - return of_register_platform_driver(&of_mpc8xxx_spi_driver); + return of_register_platform_driver(&of_fsl_spi_driver); } +module_init(fsl_spi_init); -static void __exit mpc8xxx_spi_exit(void) +static void __exit fsl_spi_exit(void) { - of_unregister_platform_driver(&of_mpc8xxx_spi_driver); + of_unregister_platform_driver(&of_fsl_spi_driver); legacy_driver_unregister(); } - -module_init(mpc8xxx_spi_init); -module_exit(mpc8xxx_spi_exit); +module_exit(fsl_spi_exit); MODULE_AUTHOR("Kumar Gala"); -MODULE_DESCRIPTION("Simple MPC8xxx SPI Driver"); +MODULE_DESCRIPTION("Simple Freescale SPI Driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.2 From 8b60d6c25b2a2d3525d5322de856c3ca408e5783 Mon Sep 17 00:00:00 2001 From: Mingkai Hu Date: Tue, 12 Oct 2010 18:18:32 +0800 Subject: spi/fsl_spi: add eSPI controller support Add eSPI controller support based on the library code spi_fsl_lib.c. The eSPI controller is newer controller 85xx/Pxxx devices supported. There're some differences comparing to the SPI controller: 1. Has different register map and different bit definition So leave the code operated the register to the driver code, not the common code. 2. Support 4 dedicated chip selects The software can't controll the chip selects directly, The SPCOM[CS] field is used to select which chip selects is used, and the SPCOM[TRANLEN] field is set to tell the controller how long the CS signal need to be asserted. So the driver doesn't need the chipselect related function when transfering data, just set corresponding register fields to controll the chipseclect. 3. Different Transmit/Receive FIFO access register behavior For SPI controller, the Tx/Rx FIFO access register can hold only one character regardless of the character length, but for eSPI controller, the register can hold 4 or 2 characters according to the character lengths. Access the Tx/Rx FIFO access register of the eSPI controller will shift out/in 4/2 characters one time. For SPI subsystem, the command and data are put into different transfers, so we need to combine all the transfers to one transfer in order to pass the transfer to eSPI controller. 4. The max transaction length limitation The max transaction length one time is limitted by the SPCOM[TRANSLEN] field which is 0xFFFF. When used mkfs.ext2 command to create ext2 filesystem on the flash, the read length will exceed the max value of the SPCOM[TRANSLEN] field. Signed-off-by: Mingkai Hu Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi_fsl_espi.c | 748 +++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/spi_fsl_lib.h | 3 + 4 files changed, 761 insertions(+) create mode 100644 drivers/spi/spi_fsl_espi.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 0e6619426ea4..9949c252c23d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -195,6 +195,15 @@ config SPI_FSL_SPI MPC83xx platform uses the controller in cpu mode or CPM/QE mode. MPC8569 uses the controller in QE mode, MPC8610 in cpu mode. +config SPI_FSL_ESPI + tristate "Freescale eSPI controller" + depends on FSL_SOC + select SPI_FSL_LIB + help + This enables using the Freescale eSPI controllers in master mode. + From MPC8536, 85xx platform uses the controller, and all P10xx, + P20xx, P30xx,P40xx, P50xx uses this controller. + config SPI_OMAP_UWIRE tristate "OMAP1 MicroWire" depends on ARCH_OMAP1 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 54bd95d63a59..557aaadf56b2 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_SPI_MPC512x_PSC) += mpc512x_psc_spi.o obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o obj-$(CONFIG_SPI_FSL_LIB) += spi_fsl_lib.o +obj-$(CONFIG_SPI_FSL_ESPI) += spi_fsl_espi.o obj-$(CONFIG_SPI_FSL_SPI) += spi_fsl_spi.o obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o diff --git a/drivers/spi/spi_fsl_espi.c b/drivers/spi/spi_fsl_espi.c new file mode 100644 index 000000000000..e3b4f6451966 --- /dev/null +++ b/drivers/spi/spi_fsl_espi.c @@ -0,0 +1,748 @@ +/* + * Freescale eSPI controller driver. + * + * Copyright 2010 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi_fsl_lib.h" + +/* eSPI Controller registers */ +struct fsl_espi_reg { + __be32 mode; /* 0x000 - eSPI mode register */ + __be32 event; /* 0x004 - eSPI event register */ + __be32 mask; /* 0x008 - eSPI mask register */ + __be32 command; /* 0x00c - eSPI command register */ + __be32 transmit; /* 0x010 - eSPI transmit FIFO access register*/ + __be32 receive; /* 0x014 - eSPI receive FIFO access register*/ + u8 res[8]; /* 0x018 - 0x01c reserved */ + __be32 csmode[4]; /* 0x020 - 0x02c eSPI cs mode register */ +}; + +struct fsl_espi_transfer { + const void *tx_buf; + void *rx_buf; + unsigned len; + unsigned n_tx; + unsigned n_rx; + unsigned actual_length; + int status; +}; + +/* eSPI Controller mode register definitions */ +#define SPMODE_ENABLE (1 << 31) +#define SPMODE_LOOP (1 << 30) +#define SPMODE_TXTHR(x) ((x) << 8) +#define SPMODE_RXTHR(x) ((x) << 0) + +/* eSPI Controller CS mode register definitions */ +#define CSMODE_CI_INACTIVEHIGH (1 << 31) +#define CSMODE_CP_BEGIN_EDGECLK (1 << 30) +#define CSMODE_REV (1 << 29) +#define CSMODE_DIV16 (1 << 28) +#define CSMODE_PM(x) ((x) << 24) +#define CSMODE_POL_1 (1 << 20) +#define CSMODE_LEN(x) ((x) << 16) +#define CSMODE_BEF(x) ((x) << 12) +#define CSMODE_AFT(x) ((x) << 8) +#define CSMODE_CG(x) ((x) << 3) + +/* Default mode/csmode for eSPI controller */ +#define SPMODE_INIT_VAL (SPMODE_TXTHR(4) | SPMODE_RXTHR(3)) +#define CSMODE_INIT_VAL (CSMODE_POL_1 | CSMODE_BEF(0) \ + | CSMODE_AFT(0) | CSMODE_CG(1)) + +/* SPIE register values */ +#define SPIE_NE 0x00000200 /* Not empty */ +#define SPIE_NF 0x00000100 /* Not full */ + +/* SPIM register values */ +#define SPIM_NE 0x00000200 /* Not empty */ +#define SPIM_NF 0x00000100 /* Not full */ +#define SPIE_RXCNT(reg) ((reg >> 24) & 0x3F) +#define SPIE_TXCNT(reg) ((reg >> 16) & 0x3F) + +/* SPCOM register values */ +#define SPCOM_CS(x) ((x) << 30) +#define SPCOM_TRANLEN(x) ((x) << 0) +#define SPCOM_TRANLEN_MAX 0xFFFF /* Max transaction length */ + +static void fsl_espi_change_mode(struct spi_device *spi) +{ + struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); + struct spi_mpc8xxx_cs *cs = spi->controller_state; + struct fsl_espi_reg *reg_base = mspi->reg_base; + __be32 __iomem *mode = ®_base->csmode[spi->chip_select]; + __be32 __iomem *espi_mode = ®_base->mode; + u32 tmp; + unsigned long flags; + + /* Turn off IRQs locally to minimize time that SPI is disabled. */ + local_irq_save(flags); + + /* Turn off SPI unit prior changing mode */ + tmp = mpc8xxx_spi_read_reg(espi_mode); + mpc8xxx_spi_write_reg(espi_mode, tmp & ~SPMODE_ENABLE); + mpc8xxx_spi_write_reg(mode, cs->hw_mode); + mpc8xxx_spi_write_reg(espi_mode, tmp); + + local_irq_restore(flags); +} + +static u32 fsl_espi_tx_buf_lsb(struct mpc8xxx_spi *mpc8xxx_spi) +{ + u32 data; + u16 data_h; + u16 data_l; + const u32 *tx = mpc8xxx_spi->tx; + + if (!tx) + return 0; + + data = *tx++ << mpc8xxx_spi->tx_shift; + data_l = data & 0xffff; + data_h = (data >> 16) & 0xffff; + swab16s(&data_l); + swab16s(&data_h); + data = data_h | data_l; + + mpc8xxx_spi->tx = tx; + return data; +} + +static int fsl_espi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + int bits_per_word = 0; + u8 pm; + u32 hz = 0; + struct spi_mpc8xxx_cs *cs = spi->controller_state; + + if (t) { + bits_per_word = t->bits_per_word; + hz = t->speed_hz; + } + + /* spi_transfer level calls that work per-word */ + if (!bits_per_word) + bits_per_word = spi->bits_per_word; + + /* Make sure its a bit width we support [4..16] */ + if ((bits_per_word < 4) || (bits_per_word > 16)) + return -EINVAL; + + if (!hz) + hz = spi->max_speed_hz; + + cs->rx_shift = 0; + cs->tx_shift = 0; + cs->get_rx = mpc8xxx_spi_rx_buf_u32; + cs->get_tx = mpc8xxx_spi_tx_buf_u32; + if (bits_per_word <= 8) { + cs->rx_shift = 8 - bits_per_word; + } else if (bits_per_word <= 16) { + cs->rx_shift = 16 - bits_per_word; + if (spi->mode & SPI_LSB_FIRST) + cs->get_tx = fsl_espi_tx_buf_lsb; + } else { + return -EINVAL; + } + + mpc8xxx_spi->rx_shift = cs->rx_shift; + mpc8xxx_spi->tx_shift = cs->tx_shift; + mpc8xxx_spi->get_rx = cs->get_rx; + mpc8xxx_spi->get_tx = cs->get_tx; + + bits_per_word = bits_per_word - 1; + + /* mask out bits we are going to set */ + cs->hw_mode &= ~(CSMODE_LEN(0xF) | CSMODE_DIV16 | CSMODE_PM(0xF)); + + cs->hw_mode |= CSMODE_LEN(bits_per_word); + + if ((mpc8xxx_spi->spibrg / hz) > 64) { + cs->hw_mode |= CSMODE_DIV16; + pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1; + + WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. " + "Will use %d Hz instead.\n", dev_name(&spi->dev), + hz, mpc8xxx_spi->spibrg / 1024); + if (pm > 16) + pm = 16; + } else { + pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1; + } + if (pm) + pm--; + + cs->hw_mode |= CSMODE_PM(pm); + + fsl_espi_change_mode(spi); + return 0; +} + +static int fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, + unsigned int len) +{ + u32 word; + struct fsl_espi_reg *reg_base = mspi->reg_base; + + mspi->count = len; + + /* enable rx ints */ + mpc8xxx_spi_write_reg(®_base->mask, SPIM_NE); + + /* transmit word */ + word = mspi->get_tx(mspi); + mpc8xxx_spi_write_reg(®_base->transmit, word); + + return 0; +} + +static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) +{ + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base; + unsigned int len = t->len; + u8 bits_per_word; + int ret; + + bits_per_word = spi->bits_per_word; + if (t->bits_per_word) + bits_per_word = t->bits_per_word; + + mpc8xxx_spi->len = t->len; + len = roundup(len, 4) / 4; + + mpc8xxx_spi->tx = t->tx_buf; + mpc8xxx_spi->rx = t->rx_buf; + + INIT_COMPLETION(mpc8xxx_spi->done); + + /* Set SPCOM[CS] and SPCOM[TRANLEN] field */ + if ((t->len - 1) > SPCOM_TRANLEN_MAX) { + dev_err(mpc8xxx_spi->dev, "Transaction length (%d)" + " beyond the SPCOM[TRANLEN] field\n", t->len); + return -EINVAL; + } + mpc8xxx_spi_write_reg(®_base->command, + (SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1))); + + ret = fsl_espi_cpu_bufs(mpc8xxx_spi, t, len); + if (ret) + return ret; + + wait_for_completion(&mpc8xxx_spi->done); + + /* disable rx ints */ + mpc8xxx_spi_write_reg(®_base->mask, 0); + + return mpc8xxx_spi->count; +} + +static void fsl_espi_addr2cmd(unsigned int addr, u8 *cmd) +{ + if (cmd[1] && cmd[2] && cmd[3]) { + cmd[1] = (u8)(addr >> 16); + cmd[2] = (u8)(addr >> 8); + cmd[3] = (u8)(addr >> 0); + } +} + +static unsigned int fsl_espi_cmd2addr(u8 *cmd) +{ + if (cmd[1] && cmd[2] && cmd[3]) + return cmd[1] << 16 | cmd[2] << 8 | cmd[3] << 0; + + return 0; +} + +static void fsl_espi_do_trans(struct spi_message *m, + struct fsl_espi_transfer *tr) +{ + struct spi_device *spi = m->spi; + struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); + struct fsl_espi_transfer *espi_trans = tr; + struct spi_message message; + struct spi_transfer *t, *first, trans; + int status = 0; + + spi_message_init(&message); + memset(&trans, 0, sizeof(trans)); + + first = list_first_entry(&m->transfers, struct spi_transfer, + transfer_list); + list_for_each_entry(t, &m->transfers, transfer_list) { + if ((first->bits_per_word != t->bits_per_word) || + (first->speed_hz != t->speed_hz)) { + espi_trans->status = -EINVAL; + dev_err(mspi->dev, "bits_per_word/speed_hz should be" + " same for the same SPI transfer\n"); + return; + } + + trans.speed_hz = t->speed_hz; + trans.bits_per_word = t->bits_per_word; + trans.delay_usecs = max(first->delay_usecs, t->delay_usecs); + } + + trans.len = espi_trans->len; + trans.tx_buf = espi_trans->tx_buf; + trans.rx_buf = espi_trans->rx_buf; + spi_message_add_tail(&trans, &message); + + list_for_each_entry(t, &message.transfers, transfer_list) { + if (t->bits_per_word || t->speed_hz) { + status = -EINVAL; + + status = fsl_espi_setup_transfer(spi, t); + if (status < 0) + break; + } + + if (t->len) + status = fsl_espi_bufs(spi, t); + + if (status) { + status = -EMSGSIZE; + break; + } + + if (t->delay_usecs) + udelay(t->delay_usecs); + } + + espi_trans->status = status; + fsl_espi_setup_transfer(spi, NULL); +} + +static void fsl_espi_cmd_trans(struct spi_message *m, + struct fsl_espi_transfer *trans, u8 *rx_buff) +{ + struct spi_transfer *t; + u8 *local_buf; + int i = 0; + struct fsl_espi_transfer *espi_trans = trans; + + local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL); + if (!local_buf) { + espi_trans->status = -ENOMEM; + return; + } + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->tx_buf) { + memcpy(local_buf + i, t->tx_buf, t->len); + i += t->len; + } + } + + espi_trans->tx_buf = local_buf; + espi_trans->rx_buf = local_buf + espi_trans->n_tx; + fsl_espi_do_trans(m, espi_trans); + + espi_trans->actual_length = espi_trans->len; + kfree(local_buf); +} + +static void fsl_espi_rw_trans(struct spi_message *m, + struct fsl_espi_transfer *trans, u8 *rx_buff) +{ + struct fsl_espi_transfer *espi_trans = trans; + unsigned int n_tx = espi_trans->n_tx; + unsigned int n_rx = espi_trans->n_rx; + struct spi_transfer *t; + u8 *local_buf; + u8 *rx_buf = rx_buff; + unsigned int trans_len; + unsigned int addr; + int i, pos, loop; + + local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL); + if (!local_buf) { + espi_trans->status = -ENOMEM; + return; + } + + for (pos = 0, loop = 0; pos < n_rx; pos += trans_len, loop++) { + trans_len = n_rx - pos; + if (trans_len > SPCOM_TRANLEN_MAX - n_tx) + trans_len = SPCOM_TRANLEN_MAX - n_tx; + + i = 0; + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->tx_buf) { + memcpy(local_buf + i, t->tx_buf, t->len); + i += t->len; + } + } + + addr = fsl_espi_cmd2addr(local_buf); + addr += pos; + fsl_espi_addr2cmd(addr, local_buf); + + espi_trans->n_tx = n_tx; + espi_trans->n_rx = trans_len; + espi_trans->len = trans_len + n_tx; + espi_trans->tx_buf = local_buf; + espi_trans->rx_buf = local_buf + n_tx; + fsl_espi_do_trans(m, espi_trans); + + memcpy(rx_buf + pos, espi_trans->rx_buf + n_tx, trans_len); + + if (loop > 0) + espi_trans->actual_length += espi_trans->len - n_tx; + else + espi_trans->actual_length += espi_trans->len; + } + + kfree(local_buf); +} + +static void fsl_espi_do_one_msg(struct spi_message *m) +{ + struct spi_transfer *t; + u8 *rx_buf = NULL; + unsigned int n_tx = 0; + unsigned int n_rx = 0; + struct fsl_espi_transfer espi_trans; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->tx_buf) + n_tx += t->len; + if (t->rx_buf) { + n_rx += t->len; + rx_buf = t->rx_buf; + } + } + + espi_trans.n_tx = n_tx; + espi_trans.n_rx = n_rx; + espi_trans.len = n_tx + n_rx; + espi_trans.actual_length = 0; + espi_trans.status = 0; + + if (!rx_buf) + fsl_espi_cmd_trans(m, &espi_trans, NULL); + else + fsl_espi_rw_trans(m, &espi_trans, rx_buf); + + m->actual_length = espi_trans.actual_length; + m->status = espi_trans.status; + m->complete(m->context); +} + +static int fsl_espi_setup(struct spi_device *spi) +{ + struct mpc8xxx_spi *mpc8xxx_spi; + struct fsl_espi_reg *reg_base; + int retval; + u32 hw_mode; + u32 loop_mode; + struct spi_mpc8xxx_cs *cs = spi->controller_state; + + if (!spi->max_speed_hz) + return -EINVAL; + + if (!cs) { + cs = kzalloc(sizeof *cs, GFP_KERNEL); + if (!cs) + return -ENOMEM; + spi->controller_state = cs; + } + + mpc8xxx_spi = spi_master_get_devdata(spi->master); + reg_base = mpc8xxx_spi->reg_base; + + hw_mode = cs->hw_mode; /* Save orginal settings */ + cs->hw_mode = mpc8xxx_spi_read_reg( + ®_base->csmode[spi->chip_select]); + /* mask out bits we are going to set */ + cs->hw_mode &= ~(CSMODE_CP_BEGIN_EDGECLK | CSMODE_CI_INACTIVEHIGH + | CSMODE_REV); + + if (spi->mode & SPI_CPHA) + cs->hw_mode |= CSMODE_CP_BEGIN_EDGECLK; + if (spi->mode & SPI_CPOL) + cs->hw_mode |= CSMODE_CI_INACTIVEHIGH; + if (!(spi->mode & SPI_LSB_FIRST)) + cs->hw_mode |= CSMODE_REV; + + /* Handle the loop mode */ + loop_mode = mpc8xxx_spi_read_reg(®_base->mode); + loop_mode &= ~SPMODE_LOOP; + if (spi->mode & SPI_LOOP) + loop_mode |= SPMODE_LOOP; + mpc8xxx_spi_write_reg(®_base->mode, loop_mode); + + retval = fsl_espi_setup_transfer(spi, NULL); + if (retval < 0) { + cs->hw_mode = hw_mode; /* Restore settings */ + return retval; + } + return 0; +} + +void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) +{ + struct fsl_espi_reg *reg_base = mspi->reg_base; + + /* We need handle RX first */ + if (events & SPIE_NE) { + u32 rx_data; + + /* Spin until RX is done */ + while (SPIE_RXCNT(events) < min(4, mspi->len)) { + cpu_relax(); + events = mpc8xxx_spi_read_reg(®_base->event); + } + mspi->len -= 4; + + rx_data = mpc8xxx_spi_read_reg(®_base->receive); + + if (mspi->rx) + mspi->get_rx(rx_data, mspi); + } + + if (!(events & SPIE_NF)) { + int ret; + + /* spin until TX is done */ + ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg( + ®_base->event)) & SPIE_NF) == 0, 1000, 0); + if (!ret) { + dev_err(mspi->dev, "tired waiting for SPIE_NF\n"); + return; + } + } + + /* Clear the events */ + mpc8xxx_spi_write_reg(®_base->event, events); + + mspi->count -= 1; + if (mspi->count) { + u32 word = mspi->get_tx(mspi); + + mpc8xxx_spi_write_reg(®_base->transmit, word); + } else { + complete(&mspi->done); + } +} + +static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) +{ + struct mpc8xxx_spi *mspi = context_data; + struct fsl_espi_reg *reg_base = mspi->reg_base; + irqreturn_t ret = IRQ_NONE; + u32 events; + + /* Get interrupt events(tx/rx) */ + events = mpc8xxx_spi_read_reg(®_base->event); + if (events) + ret = IRQ_HANDLED; + + dev_vdbg(mspi->dev, "%s: events %x\n", __func__, events); + + fsl_espi_cpu_irq(mspi, events); + + return ret; +} + +static void fsl_espi_remove(struct mpc8xxx_spi *mspi) +{ + iounmap(mspi->reg_base); +} + +static struct spi_master * __devinit fsl_espi_probe(struct device *dev, + struct resource *mem, unsigned int irq) +{ + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct spi_master *master; + struct mpc8xxx_spi *mpc8xxx_spi; + struct fsl_espi_reg *reg_base; + u32 regval; + int i, ret = 0; + + master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi)); + if (!master) { + ret = -ENOMEM; + goto err; + } + + dev_set_drvdata(dev, master); + + ret = mpc8xxx_spi_probe(dev, mem, irq); + if (ret) + goto err_probe; + + master->setup = fsl_espi_setup; + + mpc8xxx_spi = spi_master_get_devdata(master); + mpc8xxx_spi->spi_do_one_msg = fsl_espi_do_one_msg; + mpc8xxx_spi->spi_remove = fsl_espi_remove; + + mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem)); + if (!mpc8xxx_spi->reg_base) { + ret = -ENOMEM; + goto err_probe; + } + + reg_base = mpc8xxx_spi->reg_base; + + /* Register for SPI Interrupt */ + ret = request_irq(mpc8xxx_spi->irq, fsl_espi_irq, + 0, "fsl_espi", mpc8xxx_spi); + if (ret) + goto free_irq; + + if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { + mpc8xxx_spi->rx_shift = 16; + mpc8xxx_spi->tx_shift = 24; + } + + /* SPI controller initializations */ + mpc8xxx_spi_write_reg(®_base->mode, 0); + mpc8xxx_spi_write_reg(®_base->mask, 0); + mpc8xxx_spi_write_reg(®_base->command, 0); + mpc8xxx_spi_write_reg(®_base->event, 0xffffffff); + + /* Init eSPI CS mode register */ + for (i = 0; i < pdata->max_chipselect; i++) + mpc8xxx_spi_write_reg(®_base->csmode[i], CSMODE_INIT_VAL); + + /* Enable SPI interface */ + regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; + + mpc8xxx_spi_write_reg(®_base->mode, regval); + + ret = spi_register_master(master); + if (ret < 0) + goto unreg_master; + + dev_info(dev, "at 0x%p (irq = %d)\n", reg_base, mpc8xxx_spi->irq); + + return master; + +unreg_master: + free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); +free_irq: + iounmap(mpc8xxx_spi->reg_base); +err_probe: + spi_master_put(master); +err: + return ERR_PTR(ret); +} + +static int of_fsl_espi_get_chipselects(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct fsl_spi_platform_data *pdata = dev->platform_data; + const u32 *prop; + int len; + + prop = of_get_property(np, "fsl,espi-num-chipselects", &len); + if (!prop || len < sizeof(*prop)) { + dev_err(dev, "No 'fsl,espi-num-chipselects' property\n"); + return -EINVAL; + } + + pdata->max_chipselect = *prop; + pdata->cs_control = NULL; + + return 0; +} + +static int __devinit of_fsl_espi_probe(struct platform_device *ofdev, + const struct of_device_id *ofid) +{ + struct device *dev = &ofdev->dev; + struct device_node *np = ofdev->dev.of_node; + struct spi_master *master; + struct resource mem; + struct resource irq; + int ret = -ENOMEM; + + ret = of_mpc8xxx_spi_probe(ofdev, ofid); + if (ret) + return ret; + + ret = of_fsl_espi_get_chipselects(dev); + if (ret) + goto err; + + ret = of_address_to_resource(np, 0, &mem); + if (ret) + goto err; + + ret = of_irq_to_resource(np, 0, &irq); + if (!ret) { + ret = -EINVAL; + goto err; + } + + master = fsl_espi_probe(dev, &mem, irq.start); + if (IS_ERR(master)) { + ret = PTR_ERR(master); + goto err; + } + + return 0; + +err: + return ret; +} + +static int __devexit of_fsl_espi_remove(struct platform_device *dev) +{ + return mpc8xxx_spi_remove(&dev->dev); +} + +static const struct of_device_id of_fsl_espi_match[] = { + { .compatible = "fsl,mpc8536-espi" }, + {} +}; +MODULE_DEVICE_TABLE(of, of_fsl_espi_match); + +static struct of_platform_driver fsl_espi_driver = { + .driver = { + .name = "fsl_espi", + .owner = THIS_MODULE, + .of_match_table = of_fsl_espi_match, + }, + .probe = of_fsl_espi_probe, + .remove = __devexit_p(of_fsl_espi_remove), +}; + +static int __init fsl_espi_init(void) +{ + return of_register_platform_driver(&fsl_espi_driver); +} +module_init(fsl_espi_init); + +static void __exit fsl_espi_exit(void) +{ + of_unregister_platform_driver(&fsl_espi_driver); +} +module_exit(fsl_espi_exit); + +MODULE_AUTHOR("Mingkai Hu"); +MODULE_DESCRIPTION("Enhanced Freescale SPI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi_fsl_lib.h b/drivers/spi/spi_fsl_lib.h index 6ae894917387..15aa6c21c729 100644 --- a/drivers/spi/spi_fsl_lib.h +++ b/drivers/spi/spi_fsl_lib.h @@ -26,6 +26,9 @@ struct mpc8xxx_spi { /* rx & tx bufs from the spi_transfer */ const void *tx; void *rx; +#ifdef CONFIG_SPI_FSL_ESPI + int len; +#endif int subblock; struct spi_pram __iomem *pram; -- cgit v1.2.2 From 8e41b527ff2c1d7d8c9a9e8b9f53877af2892cef Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 13 Oct 2010 23:03:15 -0600 Subject: spi/topcliff: Fix uninitialized variable defect This patch fixes the following build error introduced by commit 65308c46, "spi/topcliff: cleanup for style and conciseness". drivers/spi/spi_topcliff_pch.c: In function 'pch_spi_process_messages': drivers/spi/spi_topcliff_pch.c:752: warning: 'data' is used uninitialized in +this function Signed-off-by: Grant Likely --- drivers/spi/spi_topcliff_pch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c index 97746232741e..58e187f45ec7 100644 --- a/drivers/spi/spi_topcliff_pch.c +++ b/drivers/spi/spi_topcliff_pch.c @@ -749,8 +749,8 @@ static void pch_spi_process_messages(struct work_struct *pwork) struct pch_spi_data *data; int bpw; - dev_dbg(&data->master->dev, "%s data initialized\n", __func__); data = container_of(pwork, struct pch_spi_data, work); + dev_dbg(&data->master->dev, "%s data initialized\n", __func__); spin_lock(&data->lock); -- cgit v1.2.2 From b9d228f9e896df1af787b2f3467889ab0832370a Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Wed, 13 Oct 2010 17:51:02 +0200 Subject: spi/atmel: let transfers through if not changing bits_per_word bits_per_word option in spi_transfer are allowed if it does not change the csr register. This is necessary for the driver in drivers/staging/iio/adis16260_core.c, as it uses this option. Signed-off-by: Matthias Brugger Signed-off-by: Grant Likely --- drivers/spi/atmel_spi.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index c4e04428992d..5c03a5bb168a 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -654,6 +654,8 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) struct spi_transfer *xfer; unsigned long flags; struct device *controller = spi->master->dev.parent; + u8 bits; + struct atmel_spi_device *asd; as = spi_master_get_devdata(spi->master); @@ -672,8 +674,18 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) return -EINVAL; } + if (xfer->bits_per_word) { + asd = spi->controller_state; + bits = (asd->csr >> 4) & 0xf; + if (bits != xfer->bits_per_word - 8) { + dev_dbg(&spi->dev, "you can't yet change " + "bit_per_word in transfers\n"); + return -ENOPROTOOPT; + } + } + /* FIXME implement these protocol options!! */ - if (xfer->bits_per_word || xfer->speed_hz) { + if (xfer->speed_hz) { dev_dbg(&spi->dev, "no protocol options yet\n"); return -ENOPROTOOPT; } -- cgit v1.2.2 From ef6510babf047d394aa6eb8c93baa0dd6b838c28 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Thu, 14 Oct 2010 08:55:47 -0500 Subject: spi/fsl_spi: Fix compile errors when building on ppc64 We get the following when building on ppc64 due to lack of include of : In file included from drivers/spi/spi_fsl_espi.c:25:0: drivers/spi/spi_fsl_lib.h: In function 'mpc8xxx_spi_write_reg': drivers/spi/spi_fsl_lib.h:88:2: error: implicit declaration of function 'out_be32' drivers/spi/spi_fsl_lib.h: In function 'mpc8xxx_spi_read_reg': drivers/spi/spi_fsl_lib.h:93:2: error: implicit declaration of function 'in_be32' drivers/spi/spi_fsl_espi.c: In function 'fsl_espi_remove': drivers/spi/spi_fsl_espi.c:571:2: error: implicit declaration of function 'iounmap' drivers/spi/spi_fsl_espi.c: In function 'fsl_espi_probe': drivers/spi/spi_fsl_espi.c:602:2: error: implicit declaration of function 'ioremap' drivers/spi/spi_fsl_espi.c:602:24: warning: assignment makes pointer from integer without a cast Signed-off-by: Kumar Gala Signed-off-by: Grant Likely --- drivers/spi/spi_fsl_lib.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_fsl_lib.h b/drivers/spi/spi_fsl_lib.h index 15aa6c21c729..281e060977cd 100644 --- a/drivers/spi/spi_fsl_lib.h +++ b/drivers/spi/spi_fsl_lib.h @@ -18,6 +18,8 @@ #ifndef __SPI_FSL_LIB_H__ #define __SPI_FSL_LIB_H__ +#include + /* SPI/eSPI Controller driver's private data. */ struct mpc8xxx_spi { struct device *dev; -- cgit v1.2.2 From 6038f373a3dc1f1c26496e60b6c40b164716f07e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 15 Aug 2010 18:52:59 +0200 Subject: llseek: automatically add .llseek fop All file_operations should get a .llseek operation so we can make nonseekable_open the default for future file operations without a .llseek pointer. The three cases that we can automatically detect are no_llseek, seq_lseek and default_llseek. For cases where we can we can automatically prove that the file offset is always ignored, we use noop_llseek, which maintains the current behavior of not returning an error from a seek. New drivers should normally not use noop_llseek but instead use no_llseek and call nonseekable_open at open time. Existing drivers can be converted to do the same when the maintainer knows for certain that no user code relies on calling seek on the device file. The generated code is often incorrectly indented and right now contains comments that clarify for each added line why a specific variant was chosen. In the version that gets submitted upstream, the comments will be gone and I will manually fix the indentation, because there does not seem to be a way to do that using coccinelle. Some amount of new code is currently sitting in linux-next that should get the same modifications, which I will do at the end of the merge window. Many thanks to Julia Lawall for helping me learn to write a semantic patch that does all this. ===== begin semantic patch ===== // This adds an llseek= method to all file operations, // as a preparation for making no_llseek the default. // // The rules are // - use no_llseek explicitly if we do nonseekable_open // - use seq_lseek for sequential files // - use default_llseek if we know we access f_pos // - use noop_llseek if we know we don't access f_pos, // but we still want to allow users to call lseek // @ open1 exists @ identifier nested_open; @@ nested_open(...) { <+... nonseekable_open(...) ...+> } @ open exists@ identifier open_f; identifier i, f; identifier open1.nested_open; @@ int open_f(struct inode *i, struct file *f) { <+... ( nonseekable_open(...) | nested_open(...) ) ...+> } @ read disable optional_qualifier exists @ identifier read_f; identifier f, p, s, off; type ssize_t, size_t, loff_t; expression E; identifier func; @@ ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off) { <+... ( *off = E | *off += E | func(..., off, ...) | E = *off ) ...+> } @ read_no_fpos disable optional_qualifier exists @ identifier read_f; identifier f, p, s, off; type ssize_t, size_t, loff_t; @@ ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off) { ... when != off } @ write @ identifier write_f; identifier f, p, s, off; type ssize_t, size_t, loff_t; expression E; identifier func; @@ ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off) { <+... ( *off = E | *off += E | func(..., off, ...) | E = *off ) ...+> } @ write_no_fpos @ identifier write_f; identifier f, p, s, off; type ssize_t, size_t, loff_t; @@ ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off) { ... when != off } @ fops0 @ identifier fops; @@ struct file_operations fops = { ... }; @ has_llseek depends on fops0 @ identifier fops0.fops; identifier llseek_f; @@ struct file_operations fops = { ... .llseek = llseek_f, ... }; @ has_read depends on fops0 @ identifier fops0.fops; identifier read_f; @@ struct file_operations fops = { ... .read = read_f, ... }; @ has_write depends on fops0 @ identifier fops0.fops; identifier write_f; @@ struct file_operations fops = { ... .write = write_f, ... }; @ has_open depends on fops0 @ identifier fops0.fops; identifier open_f; @@ struct file_operations fops = { ... .open = open_f, ... }; // use no_llseek if we call nonseekable_open //////////////////////////////////////////// @ nonseekable1 depends on !has_llseek && has_open @ identifier fops0.fops; identifier nso ~= "nonseekable_open"; @@ struct file_operations fops = { ... .open = nso, ... +.llseek = no_llseek, /* nonseekable */ }; @ nonseekable2 depends on !has_llseek @ identifier fops0.fops; identifier open.open_f; @@ struct file_operations fops = { ... .open = open_f, ... +.llseek = no_llseek, /* open uses nonseekable */ }; // use seq_lseek for sequential files ///////////////////////////////////// @ seq depends on !has_llseek @ identifier fops0.fops; identifier sr ~= "seq_read"; @@ struct file_operations fops = { ... .read = sr, ... +.llseek = seq_lseek, /* we have seq_read */ }; // use default_llseek if there is a readdir /////////////////////////////////////////// @ fops1 depends on !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier readdir_e; @@ // any other fop is used that changes pos struct file_operations fops = { ... .readdir = readdir_e, ... +.llseek = default_llseek, /* readdir is present */ }; // use default_llseek if at least one of read/write touches f_pos ///////////////////////////////////////////////////////////////// @ fops2 depends on !fops1 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier read.read_f; @@ // read fops use offset struct file_operations fops = { ... .read = read_f, ... +.llseek = default_llseek, /* read accesses f_pos */ }; @ fops3 depends on !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier write.write_f; @@ // write fops use offset struct file_operations fops = { ... .write = write_f, ... + .llseek = default_llseek, /* write accesses f_pos */ }; // Use noop_llseek if neither read nor write accesses f_pos /////////////////////////////////////////////////////////// @ fops4 depends on !fops1 && !fops2 && !fops3 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier read_no_fpos.read_f; identifier write_no_fpos.write_f; @@ // write fops use offset struct file_operations fops = { ... .write = write_f, .read = read_f, ... +.llseek = noop_llseek, /* read and write both use no f_pos */ }; @ depends on has_write && !has_read && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier write_no_fpos.write_f; @@ struct file_operations fops = { ... .write = write_f, ... +.llseek = noop_llseek, /* write uses no f_pos */ }; @ depends on has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier read_no_fpos.read_f; @@ struct file_operations fops = { ... .read = read_f, ... +.llseek = noop_llseek, /* read uses no f_pos */ }; @ depends on !has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; @@ struct file_operations fops = { ... +.llseek = noop_llseek, /* no read or write fn */ }; ===== End semantic patch ===== Signed-off-by: Arnd Bergmann Cc: Julia Lawall Cc: Christoph Hellwig --- drivers/spi/dw_spi.c | 1 + drivers/spi/spidev.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index d256cb00604c..a32aa66d66bf 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -131,6 +131,7 @@ static const struct file_operations mrst_spi_regs_ops = { .owner = THIS_MODULE, .open = spi_show_regs_open, .read = spi_show_regs, + .llseek = default_llseek, }; static int mrst_spi_debugfs_init(struct dw_spi *dws) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index ea1bec3c9a13..4e6245e67995 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -545,6 +545,7 @@ static const struct file_operations spidev_fops = { .unlocked_ioctl = spidev_ioctl, .open = spidev_open, .release = spidev_release, + .llseek = no_llseek, }; /*-------------------------------------------------------------------------*/ -- cgit v1.2.2 From ee2007d299ad4020115b193858817e6c57e95db5 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Sat, 16 Oct 2010 01:39:49 +0200 Subject: spi/atmel: typo in debug message This patches a typo in the debug message. Signed-off-by: Matthias Brugger Signed-off-by: Grant Likely --- drivers/spi/atmel_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 5c03a5bb168a..154529aacc03 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -679,7 +679,7 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) bits = (asd->csr >> 4) & 0xf; if (bits != xfer->bits_per_word - 8) { dev_dbg(&spi->dev, "you can't yet change " - "bit_per_word in transfers\n"); + "bits_per_word in transfers\n"); return -ENOPROTOOPT; } } -- cgit v1.2.2 From ac01e97d644da8e947ffa1bde5083290fe2e36e7 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 25 Mar 2009 00:18:35 +0000 Subject: spi/bfin_spi: fix resources leakage Re-order setup() a bit so we don't leak memory/dma/gpio resources upon errors. Also make sure we don't call kfree() twice on the same object. Signed-off-by: Daniel Mack Signed-off-by: Bryan Wu Signed-off-by: Yi Li Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 120 +++++++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 45 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 10a6dc3d37ac..4f20b923a95c 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -1006,20 +1006,24 @@ static u16 ssel[][MAX_SPI_SSEL] = { /* first setup for new devices */ static int bfin_spi_setup(struct spi_device *spi) { - struct bfin5xx_spi_chip *chip_info = NULL; - struct chip_data *chip; + struct bfin5xx_spi_chip *chip_info; + struct chip_data *chip = NULL; struct driver_data *drv_data = spi_master_get_devdata(spi->master); - int ret; + int ret = -EINVAL; if (spi->bits_per_word != 8 && spi->bits_per_word != 16) - return -EINVAL; + goto error; /* Only alloc (or use chip_info) on first setup */ + chip_info = NULL; chip = spi_get_ctldata(spi); if (chip == NULL) { - chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); - if (!chip) - return -ENOMEM; + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + dev_err(&spi->dev, "cannot allocate chip data\n"); + ret = -ENOMEM; + goto error; + } chip->enable_dma = 0; chip_info = spi->controller_data; @@ -1036,7 +1040,7 @@ static int bfin_spi_setup(struct spi_device *spi) if (chip_info->ctl_reg & (SPE|MSTR|CPOL|CPHA|LSBF|SIZE)) { dev_err(&spi->dev, "do not set bits in ctl_reg " "that the SPI framework manages\n"); - return -EINVAL; + goto error; } chip->enable_dma = chip_info->enable_dma != 0 @@ -1059,26 +1063,6 @@ static int bfin_spi_setup(struct spi_device *spi) /* we dont support running in slave mode (yet?) */ chip->ctl_reg |= MSTR; - /* - * if any one SPI chip is registered and wants DMA, request the - * DMA channel for it - */ - if (chip->enable_dma && !drv_data->dma_requested) { - /* register dma irq handler */ - if (request_dma(drv_data->dma_channel, "BFIN_SPI_DMA") < 0) { - dev_dbg(&spi->dev, - "Unable to request BlackFin SPI DMA channel\n"); - return -ENODEV; - } - if (set_dma_callback(drv_data->dma_channel, - bfin_spi_dma_irq_handler, drv_data) < 0) { - dev_dbg(&spi->dev, "Unable to set dma callback\n"); - return -EPERM; - } - dma_disable_irq(drv_data->dma_channel); - drv_data->dma_requested = 1; - } - /* * Notice: for blackfin, the speed_hz is the value of register * SPI_BAUD, not the real baudrate @@ -1087,16 +1071,6 @@ static int bfin_spi_setup(struct spi_device *spi) chip->flag = 1 << (spi->chip_select); chip->chip_select_num = spi->chip_select; - if (chip->chip_select_num == 0) { - ret = gpio_request(chip->cs_gpio, spi->modalias); - if (ret) { - if (drv_data->dma_requested) - free_dma(drv_data->dma_channel); - return ret; - } - gpio_direction_output(chip->cs_gpio, 1); - } - switch (chip->bits_per_word) { case 8: chip->n_bytes = 1; @@ -1123,9 +1097,39 @@ static int bfin_spi_setup(struct spi_device *spi) default: dev_err(&spi->dev, "%d bits_per_word is not supported\n", chip->bits_per_word); - if (chip_info) - kfree(chip); - return -ENODEV; + goto error; + } + + /* + * if any one SPI chip is registered and wants DMA, request the + * DMA channel for it + */ + if (chip->enable_dma && !drv_data->dma_requested) { + /* register dma irq handler */ + ret = request_dma(drv_data->dma_channel, "BFIN_SPI_DMA"); + if (ret) { + dev_err(&spi->dev, + "Unable to request BlackFin SPI DMA channel\n"); + goto error; + } + drv_data->dma_requested = 1; + + ret = set_dma_callback(drv_data->dma_channel, + bfin_spi_dma_irq_handler, drv_data); + if (ret) { + dev_err(&spi->dev, "Unable to set dma callback\n"); + goto error; + } + dma_disable_irq(drv_data->dma_channel); + } + + if (chip->chip_select_num == 0) { + ret = gpio_request(chip->cs_gpio, spi->modalias); + if (ret) { + dev_err(&spi->dev, "gpio_request() error\n"); + goto pin_error; + } + gpio_direction_output(chip->cs_gpio, 1); } dev_dbg(&spi->dev, "setup spi chip %s, width is %d, dma is %d\n", @@ -1136,14 +1140,38 @@ static int bfin_spi_setup(struct spi_device *spi) spi_set_ctldata(spi, chip); dev_dbg(&spi->dev, "chip select number is %d\n", chip->chip_select_num); - if ((chip->chip_select_num > 0) - && (chip->chip_select_num <= spi->master->num_chipselect)) - peripheral_request(ssel[spi->master->bus_num] - [chip->chip_select_num-1], spi->modalias); + if (chip->chip_select_num > 0 && + chip->chip_select_num <= spi->master->num_chipselect) { + ret = peripheral_request(ssel[spi->master->bus_num] + [chip->chip_select_num-1], spi->modalias); + if (ret) { + dev_err(&spi->dev, "peripheral_request() error\n"); + goto pin_error; + } + } bfin_spi_cs_deactive(drv_data, chip); return 0; + + pin_error: + if (chip->chip_select_num == 0) + gpio_free(chip->cs_gpio); + else + peripheral_free(ssel[spi->master->bus_num] + [chip->chip_select_num - 1]); + error: + if (chip) { + if (drv_data->dma_requested) + free_dma(drv_data->dma_channel); + drv_data->dma_requested = 0; + + kfree(chip); + /* prevent free 'chip' twice */ + spi_set_ctldata(spi, NULL); + } + + return ret; } /* @@ -1166,6 +1194,8 @@ static void bfin_spi_cleanup(struct spi_device *spi) gpio_free(chip->cs_gpio); kfree(chip); + /* prevent free 'chip' twice */ + spi_set_ctldata(spi, NULL); } static inline int bfin_spi_init_queue(struct driver_data *drv_data) -- cgit v1.2.2 From 60d0071b609ecb0aebe12aaedf34fe552da12c5b Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 18 May 2009 10:01:16 +0000 Subject: spi/bfin_spi: work around anomaly 05000119 Anomaly 05000119 states that the DMA_RUN bit with peripherals isn't reliable. However, the way the driver is currently written (DMA IRQ callback), we don't need the polling in the first place, so drop it. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 4f20b923a95c..61c2ebf9fe18 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -540,10 +540,6 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) clear_dma_irqstat(drv_data->dma_channel); - /* Wait for DMA to complete */ - while (get_dma_curr_irqstat(drv_data->dma_channel) & DMA_RUN) - cpu_relax(); - /* * wait for the last transaction shifted out. HRM states: * at this point there may still be data in the SPI DMA FIFO waiting -- cgit v1.2.2 From bb8beecd98de45f821a3360e0b061fc1f8da947c Mon Sep 17 00:00:00 2001 From: Wolfgang Muees Date: Fri, 22 May 2009 01:11:02 +0000 Subject: spi/bfin_spi: force sane master-mode state at boot We should make sure the SPI controller is in a sane state in case the boot loader left it in a crappy state. Such as DMA pending which causes interrupts to fire on us. When setting a sane initial state, do not default to slave mode. If we do, then the SPI peripheral may implicitly take over the SPISS pin which other things might be using. For example, the BF533-STAMP uses this pin as a GPIO to control switching between ethernet and flash. If the SPI peripheral controls the output state instead, the ethernet is no longer accessible. URL: http://blackfin.uclinux.org/gf/tracker/5630 Signed-off-by: Wolfgang Muees Signed-off-by: Michael Hennerich Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 61c2ebf9fe18..b8352546c589 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -1354,6 +1354,12 @@ static int __init bfin_spi_probe(struct platform_device *pdev) goto out_error_queue_alloc; } + /* Reset SPI registers. If these registers were used by the boot loader, + * the sky may fall on your head if you enable the dma controller. + */ + write_CTRL(drv_data, BIT_CTL_CPHA | BIT_CTL_MASTER); + write_FLAG(drv_data, 0xFF00); + /* Register with the SPI framework */ platform_set_drvdata(pdev, drv_data); status = spi_register_master(master); -- cgit v1.2.2 From f6a6d96685be6e784849d067b44acb831f595417 Mon Sep 17 00:00:00 2001 From: Yi Li Date: Wed, 3 Jun 2009 09:46:22 +0000 Subject: spi/bfin_spi: utilize the SPI interrupt in PIO mode The current behavior in PIO mode is to poll the SPI status registers which can obviously lead to higher latencies when doing a lot of SPI traffic. There is a SPI interrupt which can be used instead to signal individual completion of transactions. Signed-off-by: Yi Li Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 235 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 185 insertions(+), 50 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index b8352546c589..3736c3596a41 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -92,6 +92,9 @@ struct driver_data { dma_addr_t rx_dma; dma_addr_t tx_dma; + int irq_requested; + int spi_irq; + size_t rx_map_len; size_t tx_map_len; u8 n_bytes; @@ -115,6 +118,7 @@ struct chip_data { u16 cs_chg_udelay; /* Some devices require > 255usec delay */ u32 cs_gpio; u16 idle_tx_val; + u8 pio_interrupt; /* use spi data irq */ void (*write) (struct driver_data *); void (*read) (struct driver_data *); void (*duplex) (struct driver_data *); @@ -525,6 +529,79 @@ static void bfin_spi_giveback(struct driver_data *drv_data) msg->complete(msg->context); } +/* spi data irq handler */ +static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) +{ + struct driver_data *drv_data = dev_id; + struct chip_data *chip = drv_data->cur_chip; + struct spi_message *msg = drv_data->cur_msg; + int n_bytes = drv_data->n_bytes; + + /* wait until transfer finished. */ + while (!(read_STAT(drv_data) & BIT_STAT_RXS)) + cpu_relax(); + + if ((drv_data->tx && drv_data->tx >= drv_data->tx_end) || + (drv_data->rx && drv_data->rx >= (drv_data->rx_end - n_bytes))) { + /* last read */ + if (drv_data->rx) { + dev_dbg(&drv_data->pdev->dev, "last read\n"); + if (n_bytes == 2) + *(u16 *) (drv_data->rx) = read_RDBR(drv_data); + else if (n_bytes == 1) + *(u8 *) (drv_data->rx) = read_RDBR(drv_data); + drv_data->rx += n_bytes; + } + + msg->actual_length += drv_data->len_in_bytes; + if (drv_data->cs_change) + bfin_spi_cs_deactive(drv_data, chip); + /* Move to next transfer */ + msg->state = bfin_spi_next_transfer(drv_data); + + disable_irq(drv_data->spi_irq); + + /* Schedule transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + return IRQ_HANDLED; + } + + if (drv_data->rx && drv_data->tx) { + /* duplex */ + dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n"); + if (drv_data->n_bytes == 2) { + *(u16 *) (drv_data->rx) = read_RDBR(drv_data); + write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); + } else if (drv_data->n_bytes == 1) { + *(u8 *) (drv_data->rx) = read_RDBR(drv_data); + write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); + } + } else if (drv_data->rx) { + /* read */ + dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n"); + if (drv_data->n_bytes == 2) + *(u16 *) (drv_data->rx) = read_RDBR(drv_data); + else if (drv_data->n_bytes == 1) + *(u8 *) (drv_data->rx) = read_RDBR(drv_data); + write_TDBR(drv_data, chip->idle_tx_val); + } else if (drv_data->tx) { + /* write */ + dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n"); + bfin_spi_dummy_read(drv_data); + if (drv_data->n_bytes == 2) + write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); + else if (drv_data->n_bytes == 1) + write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); + } + + if (drv_data->tx) + drv_data->tx += n_bytes; + if (drv_data->rx) + drv_data->rx += n_bytes; + + return IRQ_HANDLED; +} + static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) { struct driver_data *drv_data = dev_id; @@ -700,6 +777,7 @@ static void bfin_spi_pump_transfers(unsigned long data) default: /* No change, the same as default setting */ + transfer->bits_per_word = chip->bits_per_word; drv_data->n_bytes = chip->n_bytes; width = chip->width; drv_data->write = drv_data->tx ? chip->write : bfin_spi_null_writer; @@ -842,60 +920,86 @@ static void bfin_spi_pump_transfers(unsigned long data) dma_enable_irq(drv_data->dma_channel); local_irq_restore(flags); - } else { - /* IO mode write then read */ - dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n"); + return; + } - /* we always use SPI_WRITE mode. SPI_READ mode - seems to have problems with setting up the - output value in TDBR prior to the transfer. */ + if (chip->pio_interrupt) { + /* use write mode. spi irq should have been disabled */ + cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD)); write_CTRL(drv_data, (cr | CFG_SPI_WRITE)); - if (full_duplex) { - /* full duplex mode */ - BUG_ON((drv_data->tx_end - drv_data->tx) != - (drv_data->rx_end - drv_data->rx)); - dev_dbg(&drv_data->pdev->dev, - "IO duplex: cr is 0x%x\n", cr); - - drv_data->duplex(drv_data); - - if (drv_data->tx != drv_data->tx_end) - tranf_success = 0; - } else if (drv_data->tx != NULL) { - /* write only half duplex */ - dev_dbg(&drv_data->pdev->dev, - "IO write: cr is 0x%x\n", cr); + /* discard old RX data and clear RXS */ + bfin_spi_dummy_read(drv_data); - drv_data->write(drv_data); + /* start transfer */ + if (drv_data->tx == NULL) + write_TDBR(drv_data, chip->idle_tx_val); + else { + if (transfer->bits_per_word == 8) + write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); + else if (transfer->bits_per_word == 16) + write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); + drv_data->tx += drv_data->n_bytes; + } - if (drv_data->tx != drv_data->tx_end) - tranf_success = 0; - } else if (drv_data->rx != NULL) { - /* read only half duplex */ - dev_dbg(&drv_data->pdev->dev, - "IO read: cr is 0x%x\n", cr); + /* once TDBR is empty, interrupt is triggered */ + enable_irq(drv_data->spi_irq); + return; + } - drv_data->read(drv_data); - if (drv_data->rx != drv_data->rx_end) - tranf_success = 0; - } + /* IO mode */ + dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n"); + + /* we always use SPI_WRITE mode. SPI_READ mode + seems to have problems with setting up the + output value in TDBR prior to the transfer. */ + write_CTRL(drv_data, (cr | CFG_SPI_WRITE)); + + if (full_duplex) { + /* full duplex mode */ + BUG_ON((drv_data->tx_end - drv_data->tx) != + (drv_data->rx_end - drv_data->rx)); + dev_dbg(&drv_data->pdev->dev, + "IO duplex: cr is 0x%x\n", cr); + + drv_data->duplex(drv_data); + + if (drv_data->tx != drv_data->tx_end) + tranf_success = 0; + } else if (drv_data->tx != NULL) { + /* write only half duplex */ + dev_dbg(&drv_data->pdev->dev, + "IO write: cr is 0x%x\n", cr); + + drv_data->write(drv_data); + + if (drv_data->tx != drv_data->tx_end) + tranf_success = 0; + } else if (drv_data->rx != NULL) { + /* read only half duplex */ + dev_dbg(&drv_data->pdev->dev, + "IO read: cr is 0x%x\n", cr); + + drv_data->read(drv_data); + if (drv_data->rx != drv_data->rx_end) + tranf_success = 0; + } - if (!tranf_success) { - dev_dbg(&drv_data->pdev->dev, - "IO write error!\n"); - message->state = ERROR_STATE; - } else { - /* Update total byte transfered */ - message->actual_length += drv_data->len_in_bytes; - /* Move to next transfer of this msg */ - message->state = bfin_spi_next_transfer(drv_data); - if (drv_data->cs_change) - bfin_spi_cs_deactive(drv_data, chip); - } - /* Schedule next transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); + if (!tranf_success) { + dev_dbg(&drv_data->pdev->dev, + "IO write error!\n"); + message->state = ERROR_STATE; + } else { + /* Update total byte transfered */ + message->actual_length += drv_data->len_in_bytes; + /* Move to next transfer of this msg */ + message->state = bfin_spi_next_transfer(drv_data); + if (drv_data->cs_change) + bfin_spi_cs_deactive(drv_data, chip); } + + /* Schedule next transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); } /* pop a msg from queue and kick off real transfer */ @@ -1047,6 +1151,7 @@ static int bfin_spi_setup(struct spi_device *spi) chip->cs_chg_udelay = chip_info->cs_chg_udelay; chip->cs_gpio = chip_info->cs_gpio; chip->idle_tx_val = chip_info->idle_tx_val; + chip->pio_interrupt = chip_info->pio_interrupt; } /* translate common spi framework into our register */ @@ -1096,6 +1201,11 @@ static int bfin_spi_setup(struct spi_device *spi) goto error; } + if (chip->enable_dma && chip->pio_interrupt) { + dev_err(&spi->dev, "enable_dma is set, " + "do not set pio_interrupt\n"); + goto error; + } /* * if any one SPI chip is registered and wants DMA, request the * DMA channel for it @@ -1119,6 +1229,18 @@ static int bfin_spi_setup(struct spi_device *spi) dma_disable_irq(drv_data->dma_channel); } + if (chip->pio_interrupt && !drv_data->irq_requested) { + ret = request_irq(drv_data->spi_irq, bfin_spi_pio_irq_handler, + IRQF_DISABLED, "BFIN_SPI", drv_data); + if (ret) { + dev_err(&spi->dev, "Unable to register spi IRQ\n"); + goto error; + } + drv_data->irq_requested = 1; + /* we use write mode, spi irq has to be disabled here */ + disable_irq(drv_data->spi_irq); + } + if (chip->chip_select_num == 0) { ret = gpio_request(chip->cs_gpio, spi->modalias); if (ret) { @@ -1328,11 +1450,19 @@ static int __init bfin_spi_probe(struct platform_device *pdev) goto out_error_ioremap; } - drv_data->dma_channel = platform_get_irq(pdev, 0); - if (drv_data->dma_channel < 0) { + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res == NULL) { dev_err(dev, "No DMA channel specified\n"); status = -ENOENT; - goto out_error_no_dma_ch; + goto out_error_free_io; + } + drv_data->dma_channel = res->start; + + drv_data->spi_irq = platform_get_irq(pdev, 0); + if (drv_data->spi_irq < 0) { + dev_err(dev, "No spi pio irq specified\n"); + status = -ENOENT; + goto out_error_free_io; } /* Initial and start queue */ @@ -1375,7 +1505,7 @@ static int __init bfin_spi_probe(struct platform_device *pdev) out_error_queue_alloc: bfin_spi_destroy_queue(drv_data); -out_error_no_dma_ch: +out_error_free_io: iounmap((void *) drv_data->regs_base); out_error_ioremap: out_error_get_res: @@ -1407,6 +1537,11 @@ static int __devexit bfin_spi_remove(struct platform_device *pdev) free_dma(drv_data->dma_channel); } + if (drv_data->irq_requested) { + free_irq(drv_data->spi_irq, drv_data); + drv_data->irq_requested = 0; + } + /* Disconnect from the SPI framework */ spi_unregister_master(drv_data->master); -- cgit v1.2.2 From 8221610e9990e7ee542a4e508d278302af8a9e75 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Wed, 17 Jun 2009 10:10:53 +0000 Subject: spi/bfin_spi: fix CS handling The CS helper functions were toggling both the Flag Enable and the Flag Value bits, but the Flag Value bit is ignored if the corresponding Flag Enable bit is cleared. So under high speed transactions, the CS sometimes would not toggle properly. Since it makes no sense to toggle the Flag Enable bit dynamically when we actually want to control the Flag Value, do this when setting up the device and then only handle toggling of the CS value during runtime. Signed-off-by: Barry Song Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 3736c3596a41..6150a8cfb82a 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -188,8 +188,7 @@ static void bfin_spi_cs_active(struct driver_data *drv_data, struct chip_data *c if (likely(chip->chip_select_num)) { u16 flag = read_FLAG(drv_data); - flag |= chip->flag; - flag &= ~(chip->flag << 8); + flag &= ~chip->flag; write_FLAG(drv_data, flag); } else { @@ -202,8 +201,7 @@ static void bfin_spi_cs_deactive(struct driver_data *drv_data, struct chip_data if (likely(chip->chip_select_num)) { u16 flag = read_FLAG(drv_data); - flag &= ~chip->flag; - flag |= (chip->flag << 8); + flag |= chip->flag; write_FLAG(drv_data, flag); } else { @@ -215,6 +213,25 @@ static void bfin_spi_cs_deactive(struct driver_data *drv_data, struct chip_data udelay(chip->cs_chg_udelay); } +/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */ +static inline void bfin_spi_cs_enable(struct driver_data *drv_data, struct chip_data *chip) +{ + u16 flag = read_FLAG(drv_data); + + flag |= (chip->flag >> 8); + + write_FLAG(drv_data, flag); +} + +static inline void bfin_spi_cs_disable(struct driver_data *drv_data, struct chip_data *chip) +{ + u16 flag = read_FLAG(drv_data); + + flag &= ~(chip->flag >> 8); + + write_FLAG(drv_data, flag); +} + /* stop controller and re-config current chip*/ static void bfin_spi_restore_state(struct driver_data *drv_data) { @@ -1169,7 +1186,7 @@ static int bfin_spi_setup(struct spi_device *spi) * SPI_BAUD, not the real baudrate */ chip->baud = hz_to_spi_baud(spi->max_speed_hz); - chip->flag = 1 << (spi->chip_select); + chip->flag = (1 << (spi->chip_select)) << 8; chip->chip_select_num = spi->chip_select; switch (chip->bits_per_word) { @@ -1268,6 +1285,7 @@ static int bfin_spi_setup(struct spi_device *spi) } } + bfin_spi_cs_enable(drv_data, chip); bfin_spi_cs_deactive(drv_data, chip); return 0; @@ -1299,14 +1317,17 @@ static int bfin_spi_setup(struct spi_device *spi) static void bfin_spi_cleanup(struct spi_device *spi) { struct chip_data *chip = spi_get_ctldata(spi); + struct driver_data *drv_data = spi_master_get_devdata(spi->master); if (!chip) return; if ((chip->chip_select_num > 0) - && (chip->chip_select_num <= spi->master->num_chipselect)) + && (chip->chip_select_num <= spi->master->num_chipselect)) { peripheral_free(ssel[spi->master->bus_num] [chip->chip_select_num-1]); + bfin_spi_cs_disable(drv_data, chip); + } if (chip->chip_select_num == 0) gpio_free(chip->cs_gpio); -- cgit v1.2.2 From 201bbc6fd84c67b4021f454d3e4c30d5cd77f702 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 23 Sep 2009 20:56:10 +0000 Subject: spi/bfin_spi: drop custom cs_change_per_word support As David points out, the cs_change_per_word option isn't standard, nor is anyone actually using it. So punt all of the dead code considering it makes up ~10% of the code size. Reported-by: David Brownell Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 148 ++++------------------------------------------ 1 file changed, 12 insertions(+), 136 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 6150a8cfb82a..f4023a78c87e 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -114,7 +114,6 @@ struct chip_data { u8 width; /* 0 or 1 */ u8 enable_dma; u8 bits_per_word; /* 8 or 16 */ - u8 cs_change_per_word; u16 cs_chg_udelay; /* Some devices require > 255usec delay */ u32 cs_gpio; u16 idle_tx_val; @@ -309,24 +308,6 @@ static void bfin_spi_u8_writer(struct driver_data *drv_data) } } -static void bfin_spi_u8_cs_chg_writer(struct driver_data *drv_data) -{ - struct chip_data *chip = drv_data->cur_chip; - - /* clear RXS (we check for RXS inside the loop) */ - bfin_spi_dummy_read(drv_data); - - while (drv_data->tx < drv_data->tx_end) { - bfin_spi_cs_active(drv_data, chip); - write_TDBR(drv_data, (*(u8 *) (drv_data->tx++))); - /* make sure transfer finished before deactiving CS */ - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - bfin_spi_dummy_read(drv_data); - bfin_spi_cs_deactive(drv_data, chip); - } -} - static void bfin_spi_u8_reader(struct driver_data *drv_data) { u16 tx_val = drv_data->cur_chip->idle_tx_val; @@ -342,24 +323,6 @@ static void bfin_spi_u8_reader(struct driver_data *drv_data) } } -static void bfin_spi_u8_cs_chg_reader(struct driver_data *drv_data) -{ - struct chip_data *chip = drv_data->cur_chip; - u16 tx_val = chip->idle_tx_val; - - /* discard old RX data and clear RXS */ - bfin_spi_dummy_read(drv_data); - - while (drv_data->rx < drv_data->rx_end) { - bfin_spi_cs_active(drv_data, chip); - write_TDBR(drv_data, tx_val); - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - *(u8 *) (drv_data->rx++) = read_RDBR(drv_data); - bfin_spi_cs_deactive(drv_data, chip); - } -} - static void bfin_spi_u8_duplex(struct driver_data *drv_data) { /* discard old RX data and clear RXS */ @@ -373,23 +336,6 @@ static void bfin_spi_u8_duplex(struct driver_data *drv_data) } } -static void bfin_spi_u8_cs_chg_duplex(struct driver_data *drv_data) -{ - struct chip_data *chip = drv_data->cur_chip; - - /* discard old RX data and clear RXS */ - bfin_spi_dummy_read(drv_data); - - while (drv_data->rx < drv_data->rx_end) { - bfin_spi_cs_active(drv_data, chip); - write_TDBR(drv_data, (*(u8 *) (drv_data->tx++))); - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - *(u8 *) (drv_data->rx++) = read_RDBR(drv_data); - bfin_spi_cs_deactive(drv_data, chip); - } -} - static void bfin_spi_u16_writer(struct driver_data *drv_data) { /* clear RXS (we check for RXS inside the loop) */ @@ -407,25 +353,6 @@ static void bfin_spi_u16_writer(struct driver_data *drv_data) } } -static void bfin_spi_u16_cs_chg_writer(struct driver_data *drv_data) -{ - struct chip_data *chip = drv_data->cur_chip; - - /* clear RXS (we check for RXS inside the loop) */ - bfin_spi_dummy_read(drv_data); - - while (drv_data->tx < drv_data->tx_end) { - bfin_spi_cs_active(drv_data, chip); - write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); - drv_data->tx += 2; - /* make sure transfer finished before deactiving CS */ - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - bfin_spi_dummy_read(drv_data); - bfin_spi_cs_deactive(drv_data, chip); - } -} - static void bfin_spi_u16_reader(struct driver_data *drv_data) { u16 tx_val = drv_data->cur_chip->idle_tx_val; @@ -442,25 +369,6 @@ static void bfin_spi_u16_reader(struct driver_data *drv_data) } } -static void bfin_spi_u16_cs_chg_reader(struct driver_data *drv_data) -{ - struct chip_data *chip = drv_data->cur_chip; - u16 tx_val = chip->idle_tx_val; - - /* discard old RX data and clear RXS */ - bfin_spi_dummy_read(drv_data); - - while (drv_data->rx < drv_data->rx_end) { - bfin_spi_cs_active(drv_data, chip); - write_TDBR(drv_data, tx_val); - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - *(u16 *) (drv_data->rx) = read_RDBR(drv_data); - drv_data->rx += 2; - bfin_spi_cs_deactive(drv_data, chip); - } -} - static void bfin_spi_u16_duplex(struct driver_data *drv_data) { /* discard old RX data and clear RXS */ @@ -476,25 +384,6 @@ static void bfin_spi_u16_duplex(struct driver_data *drv_data) } } -static void bfin_spi_u16_cs_chg_duplex(struct driver_data *drv_data) -{ - struct chip_data *chip = drv_data->cur_chip; - - /* discard old RX data and clear RXS */ - bfin_spi_dummy_read(drv_data); - - while (drv_data->rx < drv_data->rx_end) { - bfin_spi_cs_active(drv_data, chip); - write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); - drv_data->tx += 2; - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - *(u16 *) (drv_data->rx) = read_RDBR(drv_data); - drv_data->rx += 2; - bfin_spi_cs_deactive(drv_data, chip); - } -} - /* test if ther is more transfer to be done */ static void *bfin_spi_next_transfer(struct driver_data *drv_data) { @@ -773,23 +662,17 @@ static void bfin_spi_pump_transfers(unsigned long data) case 8: drv_data->n_bytes = 1; width = CFG_SPI_WORDSIZE8; - drv_data->read = chip->cs_change_per_word ? - bfin_spi_u8_cs_chg_reader : bfin_spi_u8_reader; - drv_data->write = chip->cs_change_per_word ? - bfin_spi_u8_cs_chg_writer : bfin_spi_u8_writer; - drv_data->duplex = chip->cs_change_per_word ? - bfin_spi_u8_cs_chg_duplex : bfin_spi_u8_duplex; + drv_data->read = bfin_spi_u8_reader; + drv_data->write = bfin_spi_u8_writer; + drv_data->duplex = bfin_spi_u8_duplex; break; case 16: drv_data->n_bytes = 2; width = CFG_SPI_WORDSIZE16; - drv_data->read = chip->cs_change_per_word ? - bfin_spi_u16_cs_chg_reader : bfin_spi_u16_reader; - drv_data->write = chip->cs_change_per_word ? - bfin_spi_u16_cs_chg_writer : bfin_spi_u16_writer; - drv_data->duplex = chip->cs_change_per_word ? - bfin_spi_u16_cs_chg_duplex : bfin_spi_u16_duplex; + drv_data->read = bfin_spi_u16_reader; + drv_data->write = bfin_spi_u16_writer; + drv_data->duplex = bfin_spi_u16_duplex; break; default: @@ -1164,7 +1047,6 @@ static int bfin_spi_setup(struct spi_device *spi) && drv_data->master_info->enable_dma; chip->ctl_reg = chip_info->ctl_reg; chip->bits_per_word = chip_info->bits_per_word; - chip->cs_change_per_word = chip_info->cs_change_per_word; chip->cs_chg_udelay = chip_info->cs_chg_udelay; chip->cs_gpio = chip_info->cs_gpio; chip->idle_tx_val = chip_info->idle_tx_val; @@ -1193,23 +1075,17 @@ static int bfin_spi_setup(struct spi_device *spi) case 8: chip->n_bytes = 1; chip->width = CFG_SPI_WORDSIZE8; - chip->read = chip->cs_change_per_word ? - bfin_spi_u8_cs_chg_reader : bfin_spi_u8_reader; - chip->write = chip->cs_change_per_word ? - bfin_spi_u8_cs_chg_writer : bfin_spi_u8_writer; - chip->duplex = chip->cs_change_per_word ? - bfin_spi_u8_cs_chg_duplex : bfin_spi_u8_duplex; + chip->read = bfin_spi_u8_reader; + chip->write = bfin_spi_u8_writer; + chip->duplex = bfin_spi_u8_duplex; break; case 16: chip->n_bytes = 2; chip->width = CFG_SPI_WORDSIZE16; - chip->read = chip->cs_change_per_word ? - bfin_spi_u16_cs_chg_reader : bfin_spi_u16_reader; - chip->write = chip->cs_change_per_word ? - bfin_spi_u16_cs_chg_writer : bfin_spi_u16_writer; - chip->duplex = chip->cs_change_per_word ? - bfin_spi_u16_cs_chg_duplex : bfin_spi_u16_duplex; + chip->read = bfin_spi_u16_reader; + chip->write = bfin_spi_u16_writer; + chip->duplex = bfin_spi_u16_duplex; break; default: -- cgit v1.2.2 From 5cc0159a5779f297d1b987d5f6d0feb6dc36a27a Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 23 Sep 2009 23:24:59 +0000 Subject: spi/bfin_spi: punt useless null read/write funcs The chip ops should always be initialized, so having null fallback functions are useless. Reported-by: David Brownell Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 47 +++++------------------------------------------ 1 file changed, 5 insertions(+), 42 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index f4023a78c87e..5e07f498b495 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -255,43 +255,6 @@ static inline void bfin_spi_dummy_read(struct driver_data *drv_data) (void) read_RDBR(drv_data); } -static void bfin_spi_null_writer(struct driver_data *drv_data) -{ - u8 n_bytes = drv_data->n_bytes; - u16 tx_val = drv_data->cur_chip->idle_tx_val; - - /* clear RXS (we check for RXS inside the loop) */ - bfin_spi_dummy_read(drv_data); - - while (drv_data->tx < drv_data->tx_end) { - write_TDBR(drv_data, tx_val); - drv_data->tx += n_bytes; - /* wait until transfer finished. - checking SPIF or TXS may not guarantee transfer completion */ - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - /* discard RX data and clear RXS */ - bfin_spi_dummy_read(drv_data); - } -} - -static void bfin_spi_null_reader(struct driver_data *drv_data) -{ - u8 n_bytes = drv_data->n_bytes; - u16 tx_val = drv_data->cur_chip->idle_tx_val; - - /* discard old RX data and clear RXS */ - bfin_spi_dummy_read(drv_data); - - while (drv_data->rx < drv_data->rx_end) { - write_TDBR(drv_data, tx_val); - drv_data->rx += n_bytes; - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - bfin_spi_dummy_read(drv_data); - } -} - static void bfin_spi_u8_writer(struct driver_data *drv_data) { /* clear RXS (we check for RXS inside the loop) */ @@ -680,9 +643,9 @@ static void bfin_spi_pump_transfers(unsigned long data) transfer->bits_per_word = chip->bits_per_word; drv_data->n_bytes = chip->n_bytes; width = chip->width; - drv_data->write = drv_data->tx ? chip->write : bfin_spi_null_writer; - drv_data->read = drv_data->rx ? chip->read : bfin_spi_null_reader; - drv_data->duplex = chip->duplex ? chip->duplex : bfin_spi_null_writer; + drv_data->write = chip->write; + drv_data->read = chip->read; + drv_data->duplex = chip->duplex; break; } cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD)); @@ -695,8 +658,8 @@ static void bfin_spi_pump_transfers(unsigned long data) drv_data->len = transfer->len; } dev_dbg(&drv_data->pdev->dev, - "transfer: drv_data->write is %p, chip->write is %p, null_wr is %p\n", - drv_data->write, chip->write, bfin_spi_null_writer); + "transfer: drv_data->write is %p, chip->write is %p\n", + drv_data->write, chip->write); /* speed and width has been set on per message */ message->state = RUNNING_STATE; -- cgit v1.2.2 From ab09e0406ffd42d26fc9a6dcbb626f9eb1da9160 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 23 Sep 2009 23:32:34 +0000 Subject: spi/bfin_spi: fix up some unused/misleading comments Reported-by: David Brownell Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 5e07f498b495..045c82a36164 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -44,9 +44,6 @@ MODULE_LICENSE("GPL"); #define QUEUE_RUNNING 0 #define QUEUE_STOPPED 1 -/* Value to send if no TX value is supplied */ -#define SPI_IDLE_TXVAL 0x0000 - struct driver_data { /* Driver model hookup */ struct platform_device *pdev; @@ -581,7 +578,7 @@ static void bfin_spi_pump_transfers(unsigned long data) udelay(previous->delay_usecs); } - /* Setup the transfer state based on the type of transfer */ + /* Flush any existing transfers that may be sitting in the hardware */ if (bfin_spi_flush(drv_data) == 0) { dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n"); message->status = -EIO; @@ -661,7 +658,6 @@ static void bfin_spi_pump_transfers(unsigned long data) "transfer: drv_data->write is %p, chip->write is %p\n", drv_data->write, chip->write); - /* speed and width has been set on per message */ message->state = RUNNING_STATE; dma_config = 0; @@ -966,7 +962,7 @@ static u16 ssel[][MAX_SPI_SSEL] = { P_SPI2_SSEL6, P_SPI2_SSEL7}, }; -/* first setup for new devices */ +/* setup for devices (may be called multiple times -- not just first setup) */ static int bfin_spi_setup(struct spi_device *spi) { struct bfin5xx_spi_chip *chip_info; -- cgit v1.2.2 From f4f50c3ff7815d83dcbb19341f35db2f408ac4f8 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 24 Sep 2009 00:41:49 +0000 Subject: spi/bfin_spi: convert queue run state to true/false No point in creating our own version of true/false defines when there is already a standard stdbool available to us. Reported-by: David Brownell Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 045c82a36164..40d9f64c7fa0 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -41,8 +41,6 @@ MODULE_LICENSE("GPL"); #define RUNNING_STATE ((void *)1) #define DONE_STATE ((void *)2) #define ERROR_STATE ((void *)-1) -#define QUEUE_RUNNING 0 -#define QUEUE_STOPPED 1 struct driver_data { /* Driver model hookup */ @@ -66,7 +64,7 @@ struct driver_data { spinlock_t lock; struct list_head queue; int busy; - int run; + bool running; /* Message Transfer pump */ struct tasklet_struct pump_transfers; @@ -871,7 +869,7 @@ static void bfin_spi_pump_messages(struct work_struct *work) /* Lock queue and check for queue work */ spin_lock_irqsave(&drv_data->lock, flags); - if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) { + if (list_empty(&drv_data->queue) || !drv_data->running) { /* pumper kicked off but no work to do */ drv_data->busy = 0; spin_unlock_irqrestore(&drv_data->lock, flags); @@ -926,7 +924,7 @@ static int bfin_spi_transfer(struct spi_device *spi, struct spi_message *msg) spin_lock_irqsave(&drv_data->lock, flags); - if (drv_data->run == QUEUE_STOPPED) { + if (!drv_data->running) { spin_unlock_irqrestore(&drv_data->lock, flags); return -ESHUTDOWN; } @@ -938,7 +936,7 @@ static int bfin_spi_transfer(struct spi_device *spi, struct spi_message *msg) dev_dbg(&spi->dev, "adding an msg in transfer() \n"); list_add_tail(&msg->queue, &drv_data->queue); - if (drv_data->run == QUEUE_RUNNING && !drv_data->busy) + if (drv_data->running && !drv_data->busy) queue_work(drv_data->workqueue, &drv_data->pump_messages); spin_unlock_irqrestore(&drv_data->lock, flags); @@ -1177,7 +1175,7 @@ static inline int bfin_spi_init_queue(struct driver_data *drv_data) INIT_LIST_HEAD(&drv_data->queue); spin_lock_init(&drv_data->lock); - drv_data->run = QUEUE_STOPPED; + drv_data->running = false; drv_data->busy = 0; /* init transfer tasklet */ @@ -1200,12 +1198,12 @@ static inline int bfin_spi_start_queue(struct driver_data *drv_data) spin_lock_irqsave(&drv_data->lock, flags); - if (drv_data->run == QUEUE_RUNNING || drv_data->busy) { + if (drv_data->running || drv_data->busy) { spin_unlock_irqrestore(&drv_data->lock, flags); return -EBUSY; } - drv_data->run = QUEUE_RUNNING; + drv_data->running = true; drv_data->cur_msg = NULL; drv_data->cur_transfer = NULL; drv_data->cur_chip = NULL; @@ -1230,7 +1228,7 @@ static inline int bfin_spi_stop_queue(struct driver_data *drv_data) * execution path (pump_messages) would be required to call wake_up or * friends on every SPI message. Do this instead */ - drv_data->run = QUEUE_STOPPED; + drv_data->running = false; while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) { spin_unlock_irqrestore(&drv_data->lock, flags); msleep(10); -- cgit v1.2.2 From 9c4542c7a3082bf74f00145355ef407ac82c0b3f Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 24 Sep 2009 01:04:04 +0000 Subject: spi/bfin_spi: convert read/write/duplex funcs to a dedicated ops structure Rather than having to look up the same 3 sets of functions at the same time, just use an ops structure so we only need to set one pointer. Reported-by: David Brownell Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 58 ++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 26 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 40d9f64c7fa0..b0de61a014f5 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -42,6 +42,14 @@ MODULE_LICENSE("GPL"); #define DONE_STATE ((void *)2) #define ERROR_STATE ((void *)-1) +struct driver_data; + +struct transfer_ops { + void (*write) (struct driver_data *); + void (*read) (struct driver_data *); + void (*duplex) (struct driver_data *); +}; + struct driver_data { /* Driver model hookup */ struct platform_device *pdev; @@ -94,9 +102,7 @@ struct driver_data { size_t tx_map_len; u8 n_bytes; int cs_change; - void (*write) (struct driver_data *); - void (*read) (struct driver_data *); - void (*duplex) (struct driver_data *); + const struct transfer_ops *ops; }; struct chip_data { @@ -113,9 +119,7 @@ struct chip_data { u32 cs_gpio; u16 idle_tx_val; u8 pio_interrupt; /* use spi data irq */ - void (*write) (struct driver_data *); - void (*read) (struct driver_data *); - void (*duplex) (struct driver_data *); + const struct transfer_ops *ops; }; #define DEFINE_SPI_REG(reg, off) \ @@ -294,6 +298,12 @@ static void bfin_spi_u8_duplex(struct driver_data *drv_data) } } +static const struct transfer_ops bfin_transfer_ops_u8 = { + .write = bfin_spi_u8_writer, + .read = bfin_spi_u8_reader, + .duplex = bfin_spi_u8_duplex, +}; + static void bfin_spi_u16_writer(struct driver_data *drv_data) { /* clear RXS (we check for RXS inside the loop) */ @@ -342,6 +352,12 @@ static void bfin_spi_u16_duplex(struct driver_data *drv_data) } } +static const struct transfer_ops bfin_transfer_ops_u16 = { + .write = bfin_spi_u16_writer, + .read = bfin_spi_u16_reader, + .duplex = bfin_spi_u16_duplex, +}; + /* test if ther is more transfer to be done */ static void *bfin_spi_next_transfer(struct driver_data *drv_data) { @@ -620,17 +636,13 @@ static void bfin_spi_pump_transfers(unsigned long data) case 8: drv_data->n_bytes = 1; width = CFG_SPI_WORDSIZE8; - drv_data->read = bfin_spi_u8_reader; - drv_data->write = bfin_spi_u8_writer; - drv_data->duplex = bfin_spi_u8_duplex; + drv_data->ops = &bfin_transfer_ops_u8; break; case 16: drv_data->n_bytes = 2; width = CFG_SPI_WORDSIZE16; - drv_data->read = bfin_spi_u16_reader; - drv_data->write = bfin_spi_u16_writer; - drv_data->duplex = bfin_spi_u16_duplex; + drv_data->ops = &bfin_transfer_ops_u16; break; default: @@ -638,9 +650,7 @@ static void bfin_spi_pump_transfers(unsigned long data) transfer->bits_per_word = chip->bits_per_word; drv_data->n_bytes = chip->n_bytes; width = chip->width; - drv_data->write = chip->write; - drv_data->read = chip->read; - drv_data->duplex = chip->duplex; + drv_data->ops = chip->ops; break; } cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD)); @@ -653,8 +663,8 @@ static void bfin_spi_pump_transfers(unsigned long data) drv_data->len = transfer->len; } dev_dbg(&drv_data->pdev->dev, - "transfer: drv_data->write is %p, chip->write is %p\n", - drv_data->write, chip->write); + "transfer: drv_data->ops is %p, chip->ops is %p, u8_ops is %p\n", + drv_data->ops, chip->ops, &bfin_transfer_ops_u8); message->state = RUNNING_STATE; dma_config = 0; @@ -819,7 +829,7 @@ static void bfin_spi_pump_transfers(unsigned long data) dev_dbg(&drv_data->pdev->dev, "IO duplex: cr is 0x%x\n", cr); - drv_data->duplex(drv_data); + drv_data->ops->duplex(drv_data); if (drv_data->tx != drv_data->tx_end) tranf_success = 0; @@ -828,7 +838,7 @@ static void bfin_spi_pump_transfers(unsigned long data) dev_dbg(&drv_data->pdev->dev, "IO write: cr is 0x%x\n", cr); - drv_data->write(drv_data); + drv_data->ops->write(drv_data); if (drv_data->tx != drv_data->tx_end) tranf_success = 0; @@ -837,7 +847,7 @@ static void bfin_spi_pump_transfers(unsigned long data) dev_dbg(&drv_data->pdev->dev, "IO read: cr is 0x%x\n", cr); - drv_data->read(drv_data); + drv_data->ops->read(drv_data); if (drv_data->rx != drv_data->rx_end) tranf_success = 0; } @@ -1032,17 +1042,13 @@ static int bfin_spi_setup(struct spi_device *spi) case 8: chip->n_bytes = 1; chip->width = CFG_SPI_WORDSIZE8; - chip->read = bfin_spi_u8_reader; - chip->write = bfin_spi_u8_writer; - chip->duplex = bfin_spi_u8_duplex; + chip->ops = &bfin_transfer_ops_u8; break; case 16: chip->n_bytes = 2; chip->width = CFG_SPI_WORDSIZE16; - chip->read = bfin_spi_u16_reader; - chip->write = bfin_spi_u16_writer; - chip->duplex = bfin_spi_u16_duplex; + chip->ops = &bfin_transfer_ops_u16; break; default: -- cgit v1.2.2 From b9f139a7a6a8b24e61ad94c3334eb26f34a4cb63 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 24 Sep 2009 01:27:47 +0000 Subject: spi/bfin_spi: convert struct names to something more logical The current structure names are a bit confusing as to what they represent, so use better names. Reported-by: David Brownell Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 100 +++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 50 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index b0de61a014f5..9a1801b8bf00 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -42,15 +42,15 @@ MODULE_LICENSE("GPL"); #define DONE_STATE ((void *)2) #define ERROR_STATE ((void *)-1) -struct driver_data; +struct master_data; struct transfer_ops { - void (*write) (struct driver_data *); - void (*read) (struct driver_data *); - void (*duplex) (struct driver_data *); + void (*write) (struct master_data *); + void (*read) (struct master_data *); + void (*duplex) (struct master_data *); }; -struct driver_data { +struct master_data { /* Driver model hookup */ struct platform_device *pdev; @@ -80,7 +80,7 @@ struct driver_data { /* Current message transfer state info */ struct spi_message *cur_msg; struct spi_transfer *cur_transfer; - struct chip_data *cur_chip; + struct slave_data *cur_chip; size_t len_in_bytes; size_t len; void *tx; @@ -105,7 +105,7 @@ struct driver_data { const struct transfer_ops *ops; }; -struct chip_data { +struct slave_data { u16 ctl_reg; u16 baud; u16 flag; @@ -123,9 +123,9 @@ struct chip_data { }; #define DEFINE_SPI_REG(reg, off) \ -static inline u16 read_##reg(struct driver_data *drv_data) \ +static inline u16 read_##reg(struct master_data *drv_data) \ { return bfin_read16(drv_data->regs_base + off); } \ -static inline void write_##reg(struct driver_data *drv_data, u16 v) \ +static inline void write_##reg(struct master_data *drv_data, u16 v) \ { bfin_write16(drv_data->regs_base + off, v); } DEFINE_SPI_REG(CTRL, 0x00) @@ -136,7 +136,7 @@ DEFINE_SPI_REG(RDBR, 0x10) DEFINE_SPI_REG(BAUD, 0x14) DEFINE_SPI_REG(SHAW, 0x18) -static void bfin_spi_enable(struct driver_data *drv_data) +static void bfin_spi_enable(struct master_data *drv_data) { u16 cr; @@ -144,7 +144,7 @@ static void bfin_spi_enable(struct driver_data *drv_data) write_CTRL(drv_data, (cr | BIT_CTL_ENABLE)); } -static void bfin_spi_disable(struct driver_data *drv_data) +static void bfin_spi_disable(struct master_data *drv_data) { u16 cr; @@ -167,7 +167,7 @@ static u16 hz_to_spi_baud(u32 speed_hz) return spi_baud; } -static int bfin_spi_flush(struct driver_data *drv_data) +static int bfin_spi_flush(struct master_data *drv_data) { unsigned long limit = loops_per_jiffy << 1; @@ -181,7 +181,7 @@ static int bfin_spi_flush(struct driver_data *drv_data) } /* Chip select operation functions for cs_change flag */ -static void bfin_spi_cs_active(struct driver_data *drv_data, struct chip_data *chip) +static void bfin_spi_cs_active(struct master_data *drv_data, struct slave_data *chip) { if (likely(chip->chip_select_num)) { u16 flag = read_FLAG(drv_data); @@ -194,7 +194,7 @@ static void bfin_spi_cs_active(struct driver_data *drv_data, struct chip_data *c } } -static void bfin_spi_cs_deactive(struct driver_data *drv_data, struct chip_data *chip) +static void bfin_spi_cs_deactive(struct master_data *drv_data, struct slave_data *chip) { if (likely(chip->chip_select_num)) { u16 flag = read_FLAG(drv_data); @@ -212,7 +212,7 @@ static void bfin_spi_cs_deactive(struct driver_data *drv_data, struct chip_data } /* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */ -static inline void bfin_spi_cs_enable(struct driver_data *drv_data, struct chip_data *chip) +static inline void bfin_spi_cs_enable(struct master_data *drv_data, struct slave_data *chip) { u16 flag = read_FLAG(drv_data); @@ -221,7 +221,7 @@ static inline void bfin_spi_cs_enable(struct driver_data *drv_data, struct chip_ write_FLAG(drv_data, flag); } -static inline void bfin_spi_cs_disable(struct driver_data *drv_data, struct chip_data *chip) +static inline void bfin_spi_cs_disable(struct master_data *drv_data, struct slave_data *chip) { u16 flag = read_FLAG(drv_data); @@ -231,9 +231,9 @@ static inline void bfin_spi_cs_disable(struct driver_data *drv_data, struct chip } /* stop controller and re-config current chip*/ -static void bfin_spi_restore_state(struct driver_data *drv_data) +static void bfin_spi_restore_state(struct master_data *drv_data) { - struct chip_data *chip = drv_data->cur_chip; + struct slave_data *chip = drv_data->cur_chip; /* Clear status and disable clock */ write_STAT(drv_data, BIT_STAT_CLR); @@ -249,12 +249,12 @@ static void bfin_spi_restore_state(struct driver_data *drv_data) } /* used to kick off transfer in rx mode and read unwanted RX data */ -static inline void bfin_spi_dummy_read(struct driver_data *drv_data) +static inline void bfin_spi_dummy_read(struct master_data *drv_data) { (void) read_RDBR(drv_data); } -static void bfin_spi_u8_writer(struct driver_data *drv_data) +static void bfin_spi_u8_writer(struct master_data *drv_data) { /* clear RXS (we check for RXS inside the loop) */ bfin_spi_dummy_read(drv_data); @@ -270,7 +270,7 @@ static void bfin_spi_u8_writer(struct driver_data *drv_data) } } -static void bfin_spi_u8_reader(struct driver_data *drv_data) +static void bfin_spi_u8_reader(struct master_data *drv_data) { u16 tx_val = drv_data->cur_chip->idle_tx_val; @@ -285,7 +285,7 @@ static void bfin_spi_u8_reader(struct driver_data *drv_data) } } -static void bfin_spi_u8_duplex(struct driver_data *drv_data) +static void bfin_spi_u8_duplex(struct master_data *drv_data) { /* discard old RX data and clear RXS */ bfin_spi_dummy_read(drv_data); @@ -304,7 +304,7 @@ static const struct transfer_ops bfin_transfer_ops_u8 = { .duplex = bfin_spi_u8_duplex, }; -static void bfin_spi_u16_writer(struct driver_data *drv_data) +static void bfin_spi_u16_writer(struct master_data *drv_data) { /* clear RXS (we check for RXS inside the loop) */ bfin_spi_dummy_read(drv_data); @@ -321,7 +321,7 @@ static void bfin_spi_u16_writer(struct driver_data *drv_data) } } -static void bfin_spi_u16_reader(struct driver_data *drv_data) +static void bfin_spi_u16_reader(struct master_data *drv_data) { u16 tx_val = drv_data->cur_chip->idle_tx_val; @@ -337,7 +337,7 @@ static void bfin_spi_u16_reader(struct driver_data *drv_data) } } -static void bfin_spi_u16_duplex(struct driver_data *drv_data) +static void bfin_spi_u16_duplex(struct master_data *drv_data) { /* discard old RX data and clear RXS */ bfin_spi_dummy_read(drv_data); @@ -359,7 +359,7 @@ static const struct transfer_ops bfin_transfer_ops_u16 = { }; /* test if ther is more transfer to be done */ -static void *bfin_spi_next_transfer(struct driver_data *drv_data) +static void *bfin_spi_next_transfer(struct master_data *drv_data) { struct spi_message *msg = drv_data->cur_msg; struct spi_transfer *trans = drv_data->cur_transfer; @@ -378,9 +378,9 @@ static void *bfin_spi_next_transfer(struct driver_data *drv_data) * caller already set message->status; * dma and pio irqs are blocked give finished message back */ -static void bfin_spi_giveback(struct driver_data *drv_data) +static void bfin_spi_giveback(struct master_data *drv_data) { - struct chip_data *chip = drv_data->cur_chip; + struct slave_data *chip = drv_data->cur_chip; struct spi_transfer *last_transfer; unsigned long flags; struct spi_message *msg; @@ -412,8 +412,8 @@ static void bfin_spi_giveback(struct driver_data *drv_data) /* spi data irq handler */ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) { - struct driver_data *drv_data = dev_id; - struct chip_data *chip = drv_data->cur_chip; + struct master_data *drv_data = dev_id; + struct slave_data *chip = drv_data->cur_chip; struct spi_message *msg = drv_data->cur_msg; int n_bytes = drv_data->n_bytes; @@ -484,8 +484,8 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) { - struct driver_data *drv_data = dev_id; - struct chip_data *chip = drv_data->cur_chip; + struct master_data *drv_data = dev_id; + struct slave_data *chip = drv_data->cur_chip; struct spi_message *msg = drv_data->cur_msg; unsigned long timeout; unsigned short dmastat = get_dma_curr_irqstat(drv_data->dma_channel); @@ -548,11 +548,11 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) static void bfin_spi_pump_transfers(unsigned long data) { - struct driver_data *drv_data = (struct driver_data *)data; + struct master_data *drv_data = (struct master_data *)data; struct spi_message *message = NULL; struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; - struct chip_data *chip = NULL; + struct slave_data *chip = NULL; u8 width; u16 cr, dma_width, dma_config; u32 tranf_success = 1; @@ -872,10 +872,10 @@ static void bfin_spi_pump_transfers(unsigned long data) /* pop a msg from queue and kick off real transfer */ static void bfin_spi_pump_messages(struct work_struct *work) { - struct driver_data *drv_data; + struct master_data *drv_data; unsigned long flags; - drv_data = container_of(work, struct driver_data, pump_messages); + drv_data = container_of(work, struct master_data, pump_messages); /* Lock queue and check for queue work */ spin_lock_irqsave(&drv_data->lock, flags); @@ -929,7 +929,7 @@ static void bfin_spi_pump_messages(struct work_struct *work) */ static int bfin_spi_transfer(struct spi_device *spi, struct spi_message *msg) { - struct driver_data *drv_data = spi_master_get_devdata(spi->master); + struct master_data *drv_data = spi_master_get_devdata(spi->master); unsigned long flags; spin_lock_irqsave(&drv_data->lock, flags); @@ -974,8 +974,8 @@ static u16 ssel[][MAX_SPI_SSEL] = { static int bfin_spi_setup(struct spi_device *spi) { struct bfin5xx_spi_chip *chip_info; - struct chip_data *chip = NULL; - struct driver_data *drv_data = spi_master_get_devdata(spi->master); + struct slave_data *chip = NULL; + struct master_data *drv_data = spi_master_get_devdata(spi->master); int ret = -EINVAL; if (spi->bits_per_word != 8 && spi->bits_per_word != 16) @@ -1155,8 +1155,8 @@ static int bfin_spi_setup(struct spi_device *spi) */ static void bfin_spi_cleanup(struct spi_device *spi) { - struct chip_data *chip = spi_get_ctldata(spi); - struct driver_data *drv_data = spi_master_get_devdata(spi->master); + struct slave_data *chip = spi_get_ctldata(spi); + struct master_data *drv_data = spi_master_get_devdata(spi->master); if (!chip) return; @@ -1176,7 +1176,7 @@ static void bfin_spi_cleanup(struct spi_device *spi) spi_set_ctldata(spi, NULL); } -static inline int bfin_spi_init_queue(struct driver_data *drv_data) +static inline int bfin_spi_init_queue(struct master_data *drv_data) { INIT_LIST_HEAD(&drv_data->queue); spin_lock_init(&drv_data->lock); @@ -1198,7 +1198,7 @@ static inline int bfin_spi_init_queue(struct driver_data *drv_data) return 0; } -static inline int bfin_spi_start_queue(struct driver_data *drv_data) +static inline int bfin_spi_start_queue(struct master_data *drv_data) { unsigned long flags; @@ -1220,7 +1220,7 @@ static inline int bfin_spi_start_queue(struct driver_data *drv_data) return 0; } -static inline int bfin_spi_stop_queue(struct driver_data *drv_data) +static inline int bfin_spi_stop_queue(struct master_data *drv_data) { unsigned long flags; unsigned limit = 500; @@ -1249,7 +1249,7 @@ static inline int bfin_spi_stop_queue(struct driver_data *drv_data) return status; } -static inline int bfin_spi_destroy_queue(struct driver_data *drv_data) +static inline int bfin_spi_destroy_queue(struct master_data *drv_data) { int status; @@ -1267,14 +1267,14 @@ static int __init bfin_spi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct bfin5xx_spi_master *platform_info; struct spi_master *master; - struct driver_data *drv_data = 0; + struct master_data *drv_data = 0; struct resource *res; int status = 0; platform_info = dev->platform_data; /* Allocate master with space for drv_data */ - master = spi_alloc_master(dev, sizeof(struct driver_data) + 16); + master = spi_alloc_master(dev, sizeof(struct master_data) + 16); if (!master) { dev_err(&pdev->dev, "can not alloc spi_master\n"); return -ENOMEM; @@ -1377,7 +1377,7 @@ out_error_get_res: /* stop hardware and remove the driver */ static int __devexit bfin_spi_remove(struct platform_device *pdev) { - struct driver_data *drv_data = platform_get_drvdata(pdev); + struct master_data *drv_data = platform_get_drvdata(pdev); int status = 0; if (!drv_data) @@ -1416,7 +1416,7 @@ static int __devexit bfin_spi_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int bfin_spi_suspend(struct platform_device *pdev, pm_message_t state) { - struct driver_data *drv_data = platform_get_drvdata(pdev); + struct master_data *drv_data = platform_get_drvdata(pdev); int status = 0; status = bfin_spi_stop_queue(drv_data); @@ -1431,7 +1431,7 @@ static int bfin_spi_suspend(struct platform_device *pdev, pm_message_t state) static int bfin_spi_resume(struct platform_device *pdev) { - struct driver_data *drv_data = platform_get_drvdata(pdev); + struct master_data *drv_data = platform_get_drvdata(pdev); int status = 0; /* Enable the SPI interface */ -- cgit v1.2.2 From 2a045131db69c207b9e3f9614b2c9b0f2e82bcb7 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 24 Sep 2009 01:28:54 +0000 Subject: spi/bfin_spi: drop extra memory we don't need The driver that we based ours on uses a little extra memory behind the normal driver state, but we don't. So drop this useless bit of memory. Reported-by: David Brownell Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 9a1801b8bf00..ffff0998e557 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -1267,14 +1267,14 @@ static int __init bfin_spi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct bfin5xx_spi_master *platform_info; struct spi_master *master; - struct master_data *drv_data = 0; + struct master_data *drv_data; struct resource *res; int status = 0; platform_info = dev->platform_data; /* Allocate master with space for drv_data */ - master = spi_alloc_master(dev, sizeof(struct master_data) + 16); + master = spi_alloc_master(dev, sizeof(*drv_data)); if (!master) { dev_err(&pdev->dev, "can not alloc spi_master\n"); return -ENOMEM; -- cgit v1.2.2 From 90008a641dd832cc2e2c4d21b7da94de91e9d0a4 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 15 Oct 2009 04:13:29 +0000 Subject: spi/bfin_spi: use the SPI namespaced bit names This lets us push the short SPI MMR bit names out of the global namespace. Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index ffff0998e557..07044d7db9a4 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -504,8 +504,8 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) * register until it goes low for 2 successive reads */ if (drv_data->tx != NULL) { - while ((read_STAT(drv_data) & TXS) || - (read_STAT(drv_data) & TXS)) + while ((read_STAT(drv_data) & BIT_STAT_TXS) || + (read_STAT(drv_data) & BIT_STAT_TXS)) cpu_relax(); } @@ -514,14 +514,14 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) dmastat, read_STAT(drv_data)); timeout = jiffies + HZ; - while (!(read_STAT(drv_data) & SPIF)) + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) if (!time_before(jiffies, timeout)) { dev_warn(&drv_data->pdev->dev, "timeout waiting for SPIF"); break; } else cpu_relax(); - if ((dmastat & DMA_ERR) && (spistat & RBSY)) { + if ((dmastat & DMA_ERR) && (spistat & BIT_STAT_RBSY)) { msg->state = ERROR_STATE; dev_err(&drv_data->pdev->dev, "dma receive: fifo/buffer overflow\n"); } else { @@ -1000,11 +1000,12 @@ static int bfin_spi_setup(struct spi_device *spi) if (chip_info) { /* Make sure people stop trying to set fields via ctl_reg * when they should actually be using common SPI framework. - * Currently we let through: WOM EMISO PSSE GM SZ TIMOD. + * Currently we let through: WOM EMISO PSSE GM SZ. * Not sure if a user actually needs/uses any of these, * but let's assume (for now) they do. */ - if (chip_info->ctl_reg & (SPE|MSTR|CPOL|CPHA|LSBF|SIZE)) { + if (chip_info->ctl_reg & ~(BIT_CTL_OPENDRAIN | BIT_CTL_EMISO | \ + BIT_CTL_PSSE | BIT_CTL_GM | BIT_CTL_SZ)) { dev_err(&spi->dev, "do not set bits in ctl_reg " "that the SPI framework manages\n"); goto error; @@ -1022,13 +1023,13 @@ static int bfin_spi_setup(struct spi_device *spi) /* translate common spi framework into our register */ if (spi->mode & SPI_CPOL) - chip->ctl_reg |= CPOL; + chip->ctl_reg |= BIT_CTL_CPOL; if (spi->mode & SPI_CPHA) - chip->ctl_reg |= CPHA; + chip->ctl_reg |= BIT_CTL_CPHA; if (spi->mode & SPI_LSB_FIRST) - chip->ctl_reg |= LSBF; + chip->ctl_reg |= BIT_CTL_LSBF; /* we dont support running in slave mode (yet?) */ - chip->ctl_reg |= MSTR; + chip->ctl_reg |= BIT_CTL_MASTER; /* * Notice: for blackfin, the speed_hz is the value of register -- cgit v1.2.2 From d3cc71f71ae13596cb988e16bfa2b15f09fb7347 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Tue, 17 Nov 2009 09:45:59 +0000 Subject: spi/bfin_spi: redo GPIO CS handling The common SPI layers take care of detecting CS conflicts and preventing two devices from claiming the same CS. This causes problems for the GPIO CS support we currently have as we are using CS0 to mean "GPIO CS". But if we have multiple devices using a GPIO CS, the common SPI layers see multiple devices using the virtual "CS0" and reject any such attempts. To make both work, we introduce an offset define. This represents the max number of hardware CS values that the SPI peripheral supports. If the CS is below this limit, we know we can use the hardware CS. If it's above, we treat it as a GPIO CS. This keeps the CS unique as seen by the common code and prevents conflicts. Signed-off-by: Barry Song Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 07044d7db9a4..278fe0a612c2 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -183,7 +183,7 @@ static int bfin_spi_flush(struct master_data *drv_data) /* Chip select operation functions for cs_change flag */ static void bfin_spi_cs_active(struct master_data *drv_data, struct slave_data *chip) { - if (likely(chip->chip_select_num)) { + if (likely(chip->chip_select_num < MAX_CTRL_CS)) { u16 flag = read_FLAG(drv_data); flag &= ~chip->flag; @@ -196,7 +196,7 @@ static void bfin_spi_cs_active(struct master_data *drv_data, struct slave_data * static void bfin_spi_cs_deactive(struct master_data *drv_data, struct slave_data *chip) { - if (likely(chip->chip_select_num)) { + if (likely(chip->chip_select_num < MAX_CTRL_CS)) { u16 flag = read_FLAG(drv_data); flag |= chip->flag; @@ -214,20 +214,24 @@ static void bfin_spi_cs_deactive(struct master_data *drv_data, struct slave_data /* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */ static inline void bfin_spi_cs_enable(struct master_data *drv_data, struct slave_data *chip) { - u16 flag = read_FLAG(drv_data); + if (chip->chip_select_num < MAX_CTRL_CS) { + u16 flag = read_FLAG(drv_data); - flag |= (chip->flag >> 8); + flag |= (chip->flag >> 8); - write_FLAG(drv_data, flag); + write_FLAG(drv_data, flag); + } } static inline void bfin_spi_cs_disable(struct master_data *drv_data, struct slave_data *chip) { - u16 flag = read_FLAG(drv_data); + if (chip->chip_select_num < MAX_CTRL_CS) { + u16 flag = read_FLAG(drv_data); - flag &= ~(chip->flag >> 8); + flag &= ~(chip->flag >> 8); - write_FLAG(drv_data, flag); + write_FLAG(drv_data, flag); + } } /* stop controller and re-config current chip*/ @@ -1016,7 +1020,6 @@ static int bfin_spi_setup(struct spi_device *spi) chip->ctl_reg = chip_info->ctl_reg; chip->bits_per_word = chip_info->bits_per_word; chip->cs_chg_udelay = chip_info->cs_chg_udelay; - chip->cs_gpio = chip_info->cs_gpio; chip->idle_tx_val = chip_info->idle_tx_val; chip->pio_interrupt = chip_info->pio_interrupt; } @@ -1036,8 +1039,11 @@ static int bfin_spi_setup(struct spi_device *spi) * SPI_BAUD, not the real baudrate */ chip->baud = hz_to_spi_baud(spi->max_speed_hz); - chip->flag = (1 << (spi->chip_select)) << 8; chip->chip_select_num = spi->chip_select; + if (chip->chip_select_num < MAX_CTRL_CS) + chip->flag = (1 << spi->chip_select) << 8; + else + chip->cs_gpio = chip->chip_select_num - MAX_CTRL_CS; switch (chip->bits_per_word) { case 8: @@ -1098,7 +1104,7 @@ static int bfin_spi_setup(struct spi_device *spi) disable_irq(drv_data->spi_irq); } - if (chip->chip_select_num == 0) { + if (chip->chip_select_num >= MAX_CTRL_CS) { ret = gpio_request(chip->cs_gpio, spi->modalias); if (ret) { dev_err(&spi->dev, "gpio_request() error\n"); @@ -1115,8 +1121,7 @@ static int bfin_spi_setup(struct spi_device *spi) spi_set_ctldata(spi, chip); dev_dbg(&spi->dev, "chip select number is %d\n", chip->chip_select_num); - if (chip->chip_select_num > 0 && - chip->chip_select_num <= spi->master->num_chipselect) { + if (chip->chip_select_num < MAX_CTRL_CS) { ret = peripheral_request(ssel[spi->master->bus_num] [chip->chip_select_num-1], spi->modalias); if (ret) { @@ -1131,7 +1136,7 @@ static int bfin_spi_setup(struct spi_device *spi) return 0; pin_error: - if (chip->chip_select_num == 0) + if (chip->chip_select_num >= MAX_CTRL_CS) gpio_free(chip->cs_gpio); else peripheral_free(ssel[spi->master->bus_num] @@ -1162,14 +1167,11 @@ static void bfin_spi_cleanup(struct spi_device *spi) if (!chip) return; - if ((chip->chip_select_num > 0) - && (chip->chip_select_num <= spi->master->num_chipselect)) { + if (chip->chip_select_num < MAX_CTRL_CS) { peripheral_free(ssel[spi->master->bus_num] [chip->chip_select_num-1]); bfin_spi_cs_disable(drv_data, chip); - } - - if (chip->chip_select_num == 0) + } else gpio_free(chip->cs_gpio); kfree(chip); -- cgit v1.2.2 From b052fd0a44354c655eb98fd715ef52857631dfef Mon Sep 17 00:00:00 2001 From: Barry Song Date: Wed, 18 Nov 2009 09:43:21 +0000 Subject: spi/bfin_spi: save/restore state when suspending/resuming We can't rely on the SPI_CTL/SPI_FLG registers retaining their state when suspending, so save/restore their entire values. Signed-off-by: Barry Song Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 278fe0a612c2..c23ac3b90533 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -101,6 +101,9 @@ struct master_data { size_t rx_map_len; size_t tx_map_len; u8 n_bytes; + u16 ctrl_reg; + u16 flag_reg; + int cs_change; const struct transfer_ops *ops; }; @@ -1426,8 +1429,14 @@ static int bfin_spi_suspend(struct platform_device *pdev, pm_message_t state) if (status != 0) return status; - /* stop hardware */ - bfin_spi_disable(drv_data); + drv_data->ctrl_reg = read_CTRL(drv_data); + drv_data->flag_reg = read_FLAG(drv_data); + + /* + * reset SPI_CTL and SPI_FLG registers + */ + write_CTRL(drv_data, BIT_CTL_CPHA | BIT_CTL_MASTER); + write_FLAG(drv_data, 0xFF00); return 0; } @@ -1437,8 +1446,8 @@ static int bfin_spi_resume(struct platform_device *pdev) struct master_data *drv_data = platform_get_drvdata(pdev); int status = 0; - /* Enable the SPI interface */ - bfin_spi_enable(drv_data); + write_CTRL(drv_data, drv_data->ctrl_reg); + write_FLAG(drv_data, drv_data->flag_reg); /* Start the queue running */ status = bfin_spi_start_queue(drv_data); -- cgit v1.2.2 From 9677b0de10433d31e05864dfb4bf33d0c27f752a Mon Sep 17 00:00:00 2001 From: Barry Song Date: Mon, 30 Nov 2009 03:49:41 +0000 Subject: spi/bfin_spi: sync hardware state before reprogramming everything Sometimes under load, the Blackfin core is able to send SPI register updates out before the controller is actually disabled. So when we go to reprogram the entire state (to switch to a different slave), make sure we sync after disabling the controller. Signed-off-by: Barry Song Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index c23ac3b90533..54f9dbbc52e9 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -247,6 +247,8 @@ static void bfin_spi_restore_state(struct master_data *drv_data) bfin_spi_disable(drv_data); dev_dbg(&drv_data->pdev->dev, "restoring spi ctl state\n"); + SSYNC(); + /* Load the registers */ write_CTRL(drv_data, chip->ctl_reg); write_BAUD(drv_data, chip->baud); -- cgit v1.2.2 From 7370ed6b91c37d7022a89a820b0fcd3156fa87fc Mon Sep 17 00:00:00 2001 From: Yi Li Date: Mon, 7 Dec 2009 08:07:01 +0000 Subject: spi/bfin_spi: use nosync when disabling the IRQ from the IRQ handler Using disable_irq() on the IRQ whose handler we are currently executing in can easily lead to a hang. So use the nosync variant here. Signed-off-by: Yi Li Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 54f9dbbc52e9..d446524aa894 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -448,7 +448,7 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) /* Move to next transfer */ msg->state = bfin_spi_next_transfer(drv_data); - disable_irq(drv_data->spi_irq); + disable_irq_nosync(drv_data->spi_irq); /* Schedule transfer tasklet */ tasklet_schedule(&drv_data->pump_transfers); -- cgit v1.2.2 From 033f44bd0ebca1809e8274237a64263d909f01a7 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 18 Dec 2009 17:38:04 +0000 Subject: spi/bfin_spi: push all size checks into the transfer function This reduces duplication between the setup/transfer functions and keeps values cached during setup from overriding values changed on a transfer basis (like bits_per_word). Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 57 ++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 43 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index d446524aa894..830c7055f151 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -114,10 +114,7 @@ struct slave_data { u16 flag; u8 chip_select_num; - u8 n_bytes; - u8 width; /* 0 or 1 */ u8 enable_dma; - u8 bits_per_word; /* 8 or 16 */ u16 cs_chg_udelay; /* Some devices require > 255usec delay */ u32 cs_gpio; u16 idle_tx_val; @@ -562,6 +559,7 @@ static void bfin_spi_pump_transfers(unsigned long data) struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; struct slave_data *chip = NULL; + unsigned int bits_per_word; u8 width; u16 cr, dma_width, dma_config; u32 tranf_success = 1; @@ -641,26 +639,15 @@ static void bfin_spi_pump_transfers(unsigned long data) drv_data->cs_change = transfer->cs_change; /* Bits per word setup */ - switch (transfer->bits_per_word) { - case 8: + bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word; + if (bits_per_word == 8) { drv_data->n_bytes = 1; width = CFG_SPI_WORDSIZE8; drv_data->ops = &bfin_transfer_ops_u8; - break; - - case 16: + } else { drv_data->n_bytes = 2; width = CFG_SPI_WORDSIZE16; drv_data->ops = &bfin_transfer_ops_u16; - break; - - default: - /* No change, the same as default setting */ - transfer->bits_per_word = chip->bits_per_word; - drv_data->n_bytes = chip->n_bytes; - width = chip->width; - drv_data->ops = chip->ops; - break; } cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD)); cr |= (width << 8); @@ -811,9 +798,9 @@ static void bfin_spi_pump_transfers(unsigned long data) if (drv_data->tx == NULL) write_TDBR(drv_data, chip->idle_tx_val); else { - if (transfer->bits_per_word == 8) + if (bits_per_word == 8) write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); - else if (transfer->bits_per_word == 16) + else write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); drv_data->tx += drv_data->n_bytes; } @@ -987,9 +974,6 @@ static int bfin_spi_setup(struct spi_device *spi) struct master_data *drv_data = spi_master_get_devdata(spi->master); int ret = -EINVAL; - if (spi->bits_per_word != 8 && spi->bits_per_word != 16) - goto error; - /* Only alloc (or use chip_info) on first setup */ chip_info = NULL; chip = spi_get_ctldata(spi); @@ -1023,10 +1007,16 @@ static int bfin_spi_setup(struct spi_device *spi) chip->enable_dma = chip_info->enable_dma != 0 && drv_data->master_info->enable_dma; chip->ctl_reg = chip_info->ctl_reg; - chip->bits_per_word = chip_info->bits_per_word; chip->cs_chg_udelay = chip_info->cs_chg_udelay; chip->idle_tx_val = chip_info->idle_tx_val; chip->pio_interrupt = chip_info->pio_interrupt; + spi->bits_per_word = chip_info->bits_per_word; + } + + if (spi->bits_per_word != 8 && spi->bits_per_word != 16) { + dev_err(&spi->dev, "%d bits_per_word is not supported\n", + spi->bits_per_word); + goto error; } /* translate common spi framework into our register */ @@ -1050,25 +1040,6 @@ static int bfin_spi_setup(struct spi_device *spi) else chip->cs_gpio = chip->chip_select_num - MAX_CTRL_CS; - switch (chip->bits_per_word) { - case 8: - chip->n_bytes = 1; - chip->width = CFG_SPI_WORDSIZE8; - chip->ops = &bfin_transfer_ops_u8; - break; - - case 16: - chip->n_bytes = 2; - chip->width = CFG_SPI_WORDSIZE16; - chip->ops = &bfin_transfer_ops_u16; - break; - - default: - dev_err(&spi->dev, "%d bits_per_word is not supported\n", - chip->bits_per_word); - goto error; - } - if (chip->enable_dma && chip->pio_interrupt) { dev_err(&spi->dev, "enable_dma is set, " "do not set pio_interrupt\n"); @@ -1119,7 +1090,7 @@ static int bfin_spi_setup(struct spi_device *spi) } dev_dbg(&spi->dev, "setup spi chip %s, width is %d, dma is %d\n", - spi->modalias, chip->width, chip->enable_dma); + spi->modalias, spi->bits_per_word, chip->enable_dma); dev_dbg(&spi->dev, "ctl_reg is 0x%x, flag_reg is 0x%x\n", chip->ctl_reg, chip->flag); -- cgit v1.2.2 From 5b47bcd48b3bd53c86040321de0d348aadebed87 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 18 Dec 2009 17:43:31 +0000 Subject: spi/bfin_spi: reset ctl_reg bits when setup is run again on a device During runtime, the spi setup function may be called multiple times on the same device in order to reconfigure some settings on the fly. When this happens, we need to reset the ctl_reg bits so that changing the mode works as expected. Reported-by: Andy Getzendanner Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 830c7055f151..376f2f09e34c 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -972,6 +972,7 @@ static int bfin_spi_setup(struct spi_device *spi) struct bfin5xx_spi_chip *chip_info; struct slave_data *chip = NULL; struct master_data *drv_data = spi_master_get_devdata(spi->master); + u16 bfin_ctl_reg; int ret = -EINVAL; /* Only alloc (or use chip_info) on first setup */ @@ -989,6 +990,10 @@ static int bfin_spi_setup(struct spi_device *spi) chip_info = spi->controller_data; } + /* Let people set non-standard bits directly */ + bfin_ctl_reg = BIT_CTL_OPENDRAIN | BIT_CTL_EMISO | + BIT_CTL_PSSE | BIT_CTL_GM | BIT_CTL_SZ; + /* chip_info isn't always needed */ if (chip_info) { /* Make sure people stop trying to set fields via ctl_reg @@ -997,13 +1002,11 @@ static int bfin_spi_setup(struct spi_device *spi) * Not sure if a user actually needs/uses any of these, * but let's assume (for now) they do. */ - if (chip_info->ctl_reg & ~(BIT_CTL_OPENDRAIN | BIT_CTL_EMISO | \ - BIT_CTL_PSSE | BIT_CTL_GM | BIT_CTL_SZ)) { + if (chip_info->ctl_reg & ~bfin_ctl_reg) { dev_err(&spi->dev, "do not set bits in ctl_reg " "that the SPI framework manages\n"); goto error; } - chip->enable_dma = chip_info->enable_dma != 0 && drv_data->master_info->enable_dma; chip->ctl_reg = chip_info->ctl_reg; @@ -1011,6 +1014,9 @@ static int bfin_spi_setup(struct spi_device *spi) chip->idle_tx_val = chip_info->idle_tx_val; chip->pio_interrupt = chip_info->pio_interrupt; spi->bits_per_word = chip_info->bits_per_word; + } else { + /* force a default base state */ + chip->ctl_reg &= bfin_ctl_reg; } if (spi->bits_per_word != 8 && spi->bits_per_word != 16) { -- cgit v1.2.2 From 5e8592dca303fb429d1641c205fe509f4b781ca2 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 18 Dec 2009 18:00:10 +0000 Subject: spi/bfin_spi: combine duplicate SPI_CTL read/write logic While combining things, also switch to the proper SPI bit define names. This lets us punt the rarely used SPI defines. Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 376f2f09e34c..b3450b78cc50 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -560,8 +560,7 @@ static void bfin_spi_pump_transfers(unsigned long data) struct spi_transfer *previous = NULL; struct slave_data *chip = NULL; unsigned int bits_per_word; - u8 width; - u16 cr, dma_width, dma_config; + u16 cr, cr_width, dma_width, dma_config; u32 tranf_success = 1; u8 full_duplex = 0; @@ -642,22 +641,19 @@ static void bfin_spi_pump_transfers(unsigned long data) bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word; if (bits_per_word == 8) { drv_data->n_bytes = 1; - width = CFG_SPI_WORDSIZE8; + drv_data->len = transfer->len; + cr_width = 0; drv_data->ops = &bfin_transfer_ops_u8; } else { drv_data->n_bytes = 2; - width = CFG_SPI_WORDSIZE16; + drv_data->len = (transfer->len) >> 1; + cr_width = BIT_CTL_WORDSIZE; drv_data->ops = &bfin_transfer_ops_u16; } - cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD)); - cr |= (width << 8); + cr = read_CTRL(drv_data) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE); + cr |= cr_width; write_CTRL(drv_data, cr); - if (width == CFG_SPI_WORDSIZE16) { - drv_data->len = (transfer->len) >> 1; - } else { - drv_data->len = transfer->len; - } dev_dbg(&drv_data->pdev->dev, "transfer: drv_data->ops is %p, chip->ops is %p, u8_ops is %p\n", drv_data->ops, chip->ops, &bfin_transfer_ops_u8); @@ -672,13 +668,12 @@ static void bfin_spi_pump_transfers(unsigned long data) write_BAUD(drv_data, chip->baud); write_STAT(drv_data, BIT_STAT_CLR); - cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD)); if (drv_data->cs_change) bfin_spi_cs_active(drv_data, chip); dev_dbg(&drv_data->pdev->dev, "now pumping a transfer: width is %d, len is %d\n", - width, transfer->len); + cr_width, transfer->len); /* * Try to map dma buffer and do a dma transfer. If successful use, @@ -697,7 +692,7 @@ static void bfin_spi_pump_transfers(unsigned long data) /* config dma channel */ dev_dbg(&drv_data->pdev->dev, "doing dma transfer\n"); set_dma_x_count(drv_data->dma_channel, drv_data->len); - if (width == CFG_SPI_WORDSIZE16) { + if (cr_width == BIT_CTL_WORDSIZE) { set_dma_x_modify(drv_data->dma_channel, 2); dma_width = WDSIZE_16; } else { @@ -786,10 +781,16 @@ static void bfin_spi_pump_transfers(unsigned long data) return; } + /* + * We always use SPI_WRITE mode (transfer starts with TDBR write). + * SPI_READ mode (transfer starts with RDBR read) seems to have + * problems with setting up the output value in TDBR prior to the + * start of the transfer. + */ + write_CTRL(drv_data, cr | BIT_CTL_TXMOD); + if (chip->pio_interrupt) { - /* use write mode. spi irq should have been disabled */ - cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD)); - write_CTRL(drv_data, (cr | CFG_SPI_WRITE)); + /* SPI irq should have been disabled by now */ /* discard old RX data and clear RXS */ bfin_spi_dummy_read(drv_data); @@ -813,11 +814,6 @@ static void bfin_spi_pump_transfers(unsigned long data) /* IO mode */ dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n"); - /* we always use SPI_WRITE mode. SPI_READ mode - seems to have problems with setting up the - output value in TDBR prior to the transfer. */ - write_CTRL(drv_data, (cr | CFG_SPI_WRITE)); - if (full_duplex) { /* full duplex mode */ BUG_ON((drv_data->tx_end - drv_data->tx) != -- cgit v1.2.2 From a75bd65b2189d711938d0fa7d9a79df47ddd66fa Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 22 Jan 2010 10:07:30 +0000 Subject: spi/bfin_spi: use dma_disable_irq_nosync() in irq handler Using disable_irq() on the IRQ whose handler we are currently executing in can easily lead to a hang. So use the nosync variant here. Signed-off-by: Barry Song Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index b3450b78cc50..18b313a57236 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -547,7 +547,7 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) dev_dbg(&drv_data->pdev->dev, "disable dma channel irq%d\n", drv_data->dma_channel); - dma_disable_irq(drv_data->dma_channel); + dma_disable_irq_nosync(drv_data->dma_channel); return IRQ_HANDLED; } -- cgit v1.2.2 From 7715aad4ef5cccb318942a0c47aef8a39d6130d7 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 25 Feb 2010 10:00:55 +0000 Subject: spi/bfin_spi: reject unsupported SPI modes Who knows what people will try! Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 18b313a57236..a91e556489a9 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -1022,6 +1022,10 @@ static int bfin_spi_setup(struct spi_device *spi) } /* translate common spi framework into our register */ + if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) { + dev_err(&spi->dev, "unsupported spi modes detected\n"); + goto error; + } if (spi->mode & SPI_CPOL) chip->ctl_reg |= BIT_CTL_CPOL; if (spi->mode & SPI_CPHA) -- cgit v1.2.2 From e35954053c1a2ede37f6ed1b28d626dba43dc79d Mon Sep 17 00:00:00 2001 From: Rob Maris Date: Tue, 6 Apr 2010 04:12:00 +0000 Subject: spi/bfin_spi: fix typo in comment Signed-off-by: Rob Maris Signed-off-by: Barry Song Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index a91e556489a9..550aaf70cb2d 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -364,7 +364,7 @@ static const struct transfer_ops bfin_transfer_ops_u16 = { .duplex = bfin_spi_u16_duplex, }; -/* test if ther is more transfer to be done */ +/* test if there is more transfer to be done */ static void *bfin_spi_next_transfer(struct master_data *drv_data) { struct spi_message *msg = drv_data->cur_msg; -- cgit v1.2.2 From e72dcde72c712708e2145ab0ecff539ff4a4c7fb Mon Sep 17 00:00:00 2001 From: Rob Maris Date: Tue, 6 Apr 2010 04:17:08 +0000 Subject: spi/bfin_spi: cs should be always low when a new transfer begins Signed-off-by: Rob Maris Signed-off-by: Barry Song Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 550aaf70cb2d..7882afcd580f 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -668,8 +668,7 @@ static void bfin_spi_pump_transfers(unsigned long data) write_BAUD(drv_data, chip->baud); write_STAT(drv_data, BIT_STAT_CLR); - if (drv_data->cs_change) - bfin_spi_cs_active(drv_data, chip); + bfin_spi_cs_active(drv_data, chip); dev_dbg(&drv_data->pdev->dev, "now pumping a transfer: width is %d, len is %d\n", -- cgit v1.2.2 From 4190f6a51f3f9c7c8298202d31b3ead9381c33f1 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Tue, 6 Apr 2010 03:36:24 +0000 Subject: spi/bfin_spi: warn when CS is driven by hardware (CPHA=0) When the hardware is controlling the CS, there are some SPI options we are unable to support. So issue a warning in the hopes that the user will change to a SPI mode where we can support things sanely. Signed-off-by: Barry Song Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 7882afcd580f..21de8605dbce 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -1040,9 +1040,14 @@ static int bfin_spi_setup(struct spi_device *spi) */ chip->baud = hz_to_spi_baud(spi->max_speed_hz); chip->chip_select_num = spi->chip_select; - if (chip->chip_select_num < MAX_CTRL_CS) + if (chip->chip_select_num < MAX_CTRL_CS) { + if (!(spi->mode & SPI_CPHA)) + dev_warn(&spi->dev, "Warning: SPI CPHA not set:" + " Slave Select not under software control!\n" + " See Documentation/blackfin/bfin-spi-notes.txt"); + chip->flag = (1 << spi->chip_select) << 8; - else + } else chip->cs_gpio = chip->chip_select_num - MAX_CTRL_CS; if (chip->enable_dma && chip->pio_interrupt) { -- cgit v1.2.2 From 2e768659df35ea337cb57ea3573c918d31e07894 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Fri, 17 Sep 2010 03:46:22 +0000 Subject: spi/bfin_spi: check per-transfer bits_per_word Currently, if the bits_per_word when doing a transfer is not 8bits, we always treat it as 16bits when we should actually be returning an error. Signed-off-by: Bob Liu Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 21de8605dbce..bd182145bc2c 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -644,11 +644,16 @@ static void bfin_spi_pump_transfers(unsigned long data) drv_data->len = transfer->len; cr_width = 0; drv_data->ops = &bfin_transfer_ops_u8; - } else { + } else if (bits_per_word == 16) { drv_data->n_bytes = 2; drv_data->len = (transfer->len) >> 1; cr_width = BIT_CTL_WORDSIZE; drv_data->ops = &bfin_transfer_ops_u16; + } else { + dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word\n"); + message->status = -EINVAL; + bfin_spi_giveback(drv_data); + return; } cr = read_CTRL(drv_data) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE); cr |= cr_width; -- cgit v1.2.2 From 6f7c17f4f91513247dfec52d6968cd70d5251b38 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Thu, 1 Jul 2010 14:34:10 +0000 Subject: spi/bfin_spi: init early Some systems using this bus sometimes have very basic devices on them such as regulators. So we need to be loaded even earlier in case the devices are used by things such as early board init code. Therefore register in subsys_initcall(). Signed-off-by: Michael Hennerich Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index bd182145bc2c..d17ab37d3f64 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -1466,7 +1466,7 @@ static int __init bfin_spi_init(void) { return platform_driver_probe(&bfin_spi_driver, bfin_spi_probe); } -module_init(bfin_spi_init); +subsys_initcall(bfin_spi_init); static void __exit bfin_spi_exit(void) { -- cgit v1.2.2 From 9c0a788b4315b83f6138ffa15c56ccf541106e58 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 18 Oct 2010 02:45:22 -0400 Subject: spi/bfin_spi: namespace local structs Signed-off-by: Mike Frysinger --- drivers/spi/spi_bfin5xx.c | 119 ++++++++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 58 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index d17ab37d3f64..ab483a0ec6d0 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -1,7 +1,7 @@ /* * Blackfin On-Chip SPI Driver * - * Copyright 2004-2007 Analog Devices Inc. + * Copyright 2004-2010 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -42,15 +42,15 @@ MODULE_LICENSE("GPL"); #define DONE_STATE ((void *)2) #define ERROR_STATE ((void *)-1) -struct master_data; +struct bfin_spi_master_data; -struct transfer_ops { - void (*write) (struct master_data *); - void (*read) (struct master_data *); - void (*duplex) (struct master_data *); +struct bfin_spi_transfer_ops { + void (*write) (struct bfin_spi_master_data *); + void (*read) (struct bfin_spi_master_data *); + void (*duplex) (struct bfin_spi_master_data *); }; -struct master_data { +struct bfin_spi_master_data { /* Driver model hookup */ struct platform_device *pdev; @@ -80,7 +80,7 @@ struct master_data { /* Current message transfer state info */ struct spi_message *cur_msg; struct spi_transfer *cur_transfer; - struct slave_data *cur_chip; + struct bfin_spi_slave_data *cur_chip; size_t len_in_bytes; size_t len; void *tx; @@ -105,10 +105,10 @@ struct master_data { u16 flag_reg; int cs_change; - const struct transfer_ops *ops; + const struct bfin_spi_transfer_ops *ops; }; -struct slave_data { +struct bfin_spi_slave_data { u16 ctl_reg; u16 baud; u16 flag; @@ -119,13 +119,13 @@ struct slave_data { u32 cs_gpio; u16 idle_tx_val; u8 pio_interrupt; /* use spi data irq */ - const struct transfer_ops *ops; + const struct bfin_spi_transfer_ops *ops; }; #define DEFINE_SPI_REG(reg, off) \ -static inline u16 read_##reg(struct master_data *drv_data) \ +static inline u16 read_##reg(struct bfin_spi_master_data *drv_data) \ { return bfin_read16(drv_data->regs_base + off); } \ -static inline void write_##reg(struct master_data *drv_data, u16 v) \ +static inline void write_##reg(struct bfin_spi_master_data *drv_data, u16 v) \ { bfin_write16(drv_data->regs_base + off, v); } DEFINE_SPI_REG(CTRL, 0x00) @@ -136,7 +136,7 @@ DEFINE_SPI_REG(RDBR, 0x10) DEFINE_SPI_REG(BAUD, 0x14) DEFINE_SPI_REG(SHAW, 0x18) -static void bfin_spi_enable(struct master_data *drv_data) +static void bfin_spi_enable(struct bfin_spi_master_data *drv_data) { u16 cr; @@ -144,7 +144,7 @@ static void bfin_spi_enable(struct master_data *drv_data) write_CTRL(drv_data, (cr | BIT_CTL_ENABLE)); } -static void bfin_spi_disable(struct master_data *drv_data) +static void bfin_spi_disable(struct bfin_spi_master_data *drv_data) { u16 cr; @@ -167,7 +167,7 @@ static u16 hz_to_spi_baud(u32 speed_hz) return spi_baud; } -static int bfin_spi_flush(struct master_data *drv_data) +static int bfin_spi_flush(struct bfin_spi_master_data *drv_data) { unsigned long limit = loops_per_jiffy << 1; @@ -181,7 +181,7 @@ static int bfin_spi_flush(struct master_data *drv_data) } /* Chip select operation functions for cs_change flag */ -static void bfin_spi_cs_active(struct master_data *drv_data, struct slave_data *chip) +static void bfin_spi_cs_active(struct bfin_spi_master_data *drv_data, struct bfin_spi_slave_data *chip) { if (likely(chip->chip_select_num < MAX_CTRL_CS)) { u16 flag = read_FLAG(drv_data); @@ -194,7 +194,8 @@ static void bfin_spi_cs_active(struct master_data *drv_data, struct slave_data * } } -static void bfin_spi_cs_deactive(struct master_data *drv_data, struct slave_data *chip) +static void bfin_spi_cs_deactive(struct bfin_spi_master_data *drv_data, + struct bfin_spi_slave_data *chip) { if (likely(chip->chip_select_num < MAX_CTRL_CS)) { u16 flag = read_FLAG(drv_data); @@ -212,7 +213,8 @@ static void bfin_spi_cs_deactive(struct master_data *drv_data, struct slave_data } /* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */ -static inline void bfin_spi_cs_enable(struct master_data *drv_data, struct slave_data *chip) +static inline void bfin_spi_cs_enable(struct bfin_spi_master_data *drv_data, + struct bfin_spi_slave_data *chip) { if (chip->chip_select_num < MAX_CTRL_CS) { u16 flag = read_FLAG(drv_data); @@ -223,7 +225,8 @@ static inline void bfin_spi_cs_enable(struct master_data *drv_data, struct slave } } -static inline void bfin_spi_cs_disable(struct master_data *drv_data, struct slave_data *chip) +static inline void bfin_spi_cs_disable(struct bfin_spi_master_data *drv_data, + struct bfin_spi_slave_data *chip) { if (chip->chip_select_num < MAX_CTRL_CS) { u16 flag = read_FLAG(drv_data); @@ -235,9 +238,9 @@ static inline void bfin_spi_cs_disable(struct master_data *drv_data, struct slav } /* stop controller and re-config current chip*/ -static void bfin_spi_restore_state(struct master_data *drv_data) +static void bfin_spi_restore_state(struct bfin_spi_master_data *drv_data) { - struct slave_data *chip = drv_data->cur_chip; + struct bfin_spi_slave_data *chip = drv_data->cur_chip; /* Clear status and disable clock */ write_STAT(drv_data, BIT_STAT_CLR); @@ -255,12 +258,12 @@ static void bfin_spi_restore_state(struct master_data *drv_data) } /* used to kick off transfer in rx mode and read unwanted RX data */ -static inline void bfin_spi_dummy_read(struct master_data *drv_data) +static inline void bfin_spi_dummy_read(struct bfin_spi_master_data *drv_data) { (void) read_RDBR(drv_data); } -static void bfin_spi_u8_writer(struct master_data *drv_data) +static void bfin_spi_u8_writer(struct bfin_spi_master_data *drv_data) { /* clear RXS (we check for RXS inside the loop) */ bfin_spi_dummy_read(drv_data); @@ -276,7 +279,7 @@ static void bfin_spi_u8_writer(struct master_data *drv_data) } } -static void bfin_spi_u8_reader(struct master_data *drv_data) +static void bfin_spi_u8_reader(struct bfin_spi_master_data *drv_data) { u16 tx_val = drv_data->cur_chip->idle_tx_val; @@ -291,7 +294,7 @@ static void bfin_spi_u8_reader(struct master_data *drv_data) } } -static void bfin_spi_u8_duplex(struct master_data *drv_data) +static void bfin_spi_u8_duplex(struct bfin_spi_master_data *drv_data) { /* discard old RX data and clear RXS */ bfin_spi_dummy_read(drv_data); @@ -304,13 +307,13 @@ static void bfin_spi_u8_duplex(struct master_data *drv_data) } } -static const struct transfer_ops bfin_transfer_ops_u8 = { +static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u8 = { .write = bfin_spi_u8_writer, .read = bfin_spi_u8_reader, .duplex = bfin_spi_u8_duplex, }; -static void bfin_spi_u16_writer(struct master_data *drv_data) +static void bfin_spi_u16_writer(struct bfin_spi_master_data *drv_data) { /* clear RXS (we check for RXS inside the loop) */ bfin_spi_dummy_read(drv_data); @@ -327,7 +330,7 @@ static void bfin_spi_u16_writer(struct master_data *drv_data) } } -static void bfin_spi_u16_reader(struct master_data *drv_data) +static void bfin_spi_u16_reader(struct bfin_spi_master_data *drv_data) { u16 tx_val = drv_data->cur_chip->idle_tx_val; @@ -343,7 +346,7 @@ static void bfin_spi_u16_reader(struct master_data *drv_data) } } -static void bfin_spi_u16_duplex(struct master_data *drv_data) +static void bfin_spi_u16_duplex(struct bfin_spi_master_data *drv_data) { /* discard old RX data and clear RXS */ bfin_spi_dummy_read(drv_data); @@ -358,14 +361,14 @@ static void bfin_spi_u16_duplex(struct master_data *drv_data) } } -static const struct transfer_ops bfin_transfer_ops_u16 = { +static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u16 = { .write = bfin_spi_u16_writer, .read = bfin_spi_u16_reader, .duplex = bfin_spi_u16_duplex, }; /* test if there is more transfer to be done */ -static void *bfin_spi_next_transfer(struct master_data *drv_data) +static void *bfin_spi_next_transfer(struct bfin_spi_master_data *drv_data) { struct spi_message *msg = drv_data->cur_msg; struct spi_transfer *trans = drv_data->cur_transfer; @@ -384,9 +387,9 @@ static void *bfin_spi_next_transfer(struct master_data *drv_data) * caller already set message->status; * dma and pio irqs are blocked give finished message back */ -static void bfin_spi_giveback(struct master_data *drv_data) +static void bfin_spi_giveback(struct bfin_spi_master_data *drv_data) { - struct slave_data *chip = drv_data->cur_chip; + struct bfin_spi_slave_data *chip = drv_data->cur_chip; struct spi_transfer *last_transfer; unsigned long flags; struct spi_message *msg; @@ -418,8 +421,8 @@ static void bfin_spi_giveback(struct master_data *drv_data) /* spi data irq handler */ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) { - struct master_data *drv_data = dev_id; - struct slave_data *chip = drv_data->cur_chip; + struct bfin_spi_master_data *drv_data = dev_id; + struct bfin_spi_slave_data *chip = drv_data->cur_chip; struct spi_message *msg = drv_data->cur_msg; int n_bytes = drv_data->n_bytes; @@ -490,8 +493,8 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) { - struct master_data *drv_data = dev_id; - struct slave_data *chip = drv_data->cur_chip; + struct bfin_spi_master_data *drv_data = dev_id; + struct bfin_spi_slave_data *chip = drv_data->cur_chip; struct spi_message *msg = drv_data->cur_msg; unsigned long timeout; unsigned short dmastat = get_dma_curr_irqstat(drv_data->dma_channel); @@ -554,11 +557,11 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) static void bfin_spi_pump_transfers(unsigned long data) { - struct master_data *drv_data = (struct master_data *)data; + struct bfin_spi_master_data *drv_data = (struct bfin_spi_master_data *)data; struct spi_message *message = NULL; struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; - struct slave_data *chip = NULL; + struct bfin_spi_slave_data *chip = NULL; unsigned int bits_per_word; u16 cr, cr_width, dma_width, dma_config; u32 tranf_success = 1; @@ -643,12 +646,12 @@ static void bfin_spi_pump_transfers(unsigned long data) drv_data->n_bytes = 1; drv_data->len = transfer->len; cr_width = 0; - drv_data->ops = &bfin_transfer_ops_u8; + drv_data->ops = &bfin_bfin_spi_transfer_ops_u8; } else if (bits_per_word == 16) { drv_data->n_bytes = 2; drv_data->len = (transfer->len) >> 1; cr_width = BIT_CTL_WORDSIZE; - drv_data->ops = &bfin_transfer_ops_u16; + drv_data->ops = &bfin_bfin_spi_transfer_ops_u16; } else { dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word\n"); message->status = -EINVAL; @@ -661,7 +664,7 @@ static void bfin_spi_pump_transfers(unsigned long data) dev_dbg(&drv_data->pdev->dev, "transfer: drv_data->ops is %p, chip->ops is %p, u8_ops is %p\n", - drv_data->ops, chip->ops, &bfin_transfer_ops_u8); + drv_data->ops, chip->ops, &bfin_bfin_spi_transfer_ops_u8); message->state = RUNNING_STATE; dma_config = 0; @@ -868,10 +871,10 @@ static void bfin_spi_pump_transfers(unsigned long data) /* pop a msg from queue and kick off real transfer */ static void bfin_spi_pump_messages(struct work_struct *work) { - struct master_data *drv_data; + struct bfin_spi_master_data *drv_data; unsigned long flags; - drv_data = container_of(work, struct master_data, pump_messages); + drv_data = container_of(work, struct bfin_spi_master_data, pump_messages); /* Lock queue and check for queue work */ spin_lock_irqsave(&drv_data->lock, flags); @@ -925,7 +928,7 @@ static void bfin_spi_pump_messages(struct work_struct *work) */ static int bfin_spi_transfer(struct spi_device *spi, struct spi_message *msg) { - struct master_data *drv_data = spi_master_get_devdata(spi->master); + struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master); unsigned long flags; spin_lock_irqsave(&drv_data->lock, flags); @@ -970,8 +973,8 @@ static u16 ssel[][MAX_SPI_SSEL] = { static int bfin_spi_setup(struct spi_device *spi) { struct bfin5xx_spi_chip *chip_info; - struct slave_data *chip = NULL; - struct master_data *drv_data = spi_master_get_devdata(spi->master); + struct bfin_spi_slave_data *chip = NULL; + struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master); u16 bfin_ctl_reg; int ret = -EINVAL; @@ -1152,8 +1155,8 @@ static int bfin_spi_setup(struct spi_device *spi) */ static void bfin_spi_cleanup(struct spi_device *spi) { - struct slave_data *chip = spi_get_ctldata(spi); - struct master_data *drv_data = spi_master_get_devdata(spi->master); + struct bfin_spi_slave_data *chip = spi_get_ctldata(spi); + struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master); if (!chip) return; @@ -1170,7 +1173,7 @@ static void bfin_spi_cleanup(struct spi_device *spi) spi_set_ctldata(spi, NULL); } -static inline int bfin_spi_init_queue(struct master_data *drv_data) +static inline int bfin_spi_init_queue(struct bfin_spi_master_data *drv_data) { INIT_LIST_HEAD(&drv_data->queue); spin_lock_init(&drv_data->lock); @@ -1192,7 +1195,7 @@ static inline int bfin_spi_init_queue(struct master_data *drv_data) return 0; } -static inline int bfin_spi_start_queue(struct master_data *drv_data) +static inline int bfin_spi_start_queue(struct bfin_spi_master_data *drv_data) { unsigned long flags; @@ -1214,7 +1217,7 @@ static inline int bfin_spi_start_queue(struct master_data *drv_data) return 0; } -static inline int bfin_spi_stop_queue(struct master_data *drv_data) +static inline int bfin_spi_stop_queue(struct bfin_spi_master_data *drv_data) { unsigned long flags; unsigned limit = 500; @@ -1243,7 +1246,7 @@ static inline int bfin_spi_stop_queue(struct master_data *drv_data) return status; } -static inline int bfin_spi_destroy_queue(struct master_data *drv_data) +static inline int bfin_spi_destroy_queue(struct bfin_spi_master_data *drv_data) { int status; @@ -1261,7 +1264,7 @@ static int __init bfin_spi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct bfin5xx_spi_master *platform_info; struct spi_master *master; - struct master_data *drv_data; + struct bfin_spi_master_data *drv_data; struct resource *res; int status = 0; @@ -1371,7 +1374,7 @@ out_error_get_res: /* stop hardware and remove the driver */ static int __devexit bfin_spi_remove(struct platform_device *pdev) { - struct master_data *drv_data = platform_get_drvdata(pdev); + struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev); int status = 0; if (!drv_data) @@ -1410,7 +1413,7 @@ static int __devexit bfin_spi_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int bfin_spi_suspend(struct platform_device *pdev, pm_message_t state) { - struct master_data *drv_data = platform_get_drvdata(pdev); + struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev); int status = 0; status = bfin_spi_stop_queue(drv_data); @@ -1431,7 +1434,7 @@ static int bfin_spi_suspend(struct platform_device *pdev, pm_message_t state) static int bfin_spi_resume(struct platform_device *pdev) { - struct master_data *drv_data = platform_get_drvdata(pdev); + struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev); int status = 0; write_CTRL(drv_data, drv_data->ctrl_reg); -- cgit v1.2.2 From e1993ed6420afd4421336d75e73641f75da87a7f Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 19 Oct 2010 18:03:27 +0800 Subject: spi/omap2_mcspi: disable channel after TX_ONLY transfer in PIO mode In the TX_ONLY transfer, the SPI controller also receives data simultaneously and saves them in the rx register. After the TX_ONLY transfer, the rx register will hold the random data received during the last tx transaction. If the direct following transfer is RX_ONLY, this random data has the possibility to affect this transfer like this: When the SPI controller is changed from TX_ONLY to RX_ONLY, the random data makes the rx register full immediately and triggers a dummy write automatically(in SPI RX_ONLY transfers, we need a dummy write to trigger the first transaction). So the first data received in the RX_ONLY transfer will be that random data instead of something meaningful. We can avoid this by inserting a Disable/Re-enable toggle of the channel after the TX_ONLY transfer, since it purges the rx register. Signed-off-by: Jason Wang Tested-by: Grazvydas Ignotas Acked-by: Tony Lindgren Signed-off-by: Grant Likely --- drivers/spi/omap2_mcspi.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index d70392795055..27a3fa1f3bf4 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -626,6 +626,12 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) } else if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_EOT) < 0) dev_err(&spi->dev, "EOT timed out\n"); + + /* disable chan to purge rx datas received in TX_ONLY transfer, + * otherwise these rx datas will affect the direct following + * RX_ONLY transfer. + */ + omap2_mcspi_set_enable(spi, 0); } out: omap2_mcspi_set_enable(spi, 1); -- cgit v1.2.2 From 2764c500be0c1f057349ee6c81557239de060f87 Mon Sep 17 00:00:00 2001 From: Ilkka Koskinen Date: Tue, 19 Oct 2010 17:07:31 +0300 Subject: spi/omap2_mcspi: Verify TX reg is empty after TX only xfer with DMA In case of TX only with DMA, the driver assumes that the data has been transferred once DMA callback in invoked. However, SPI's shift register may still contain data. Thus, the driver is supposed to verify that the register is empty and the end of the SPI transfer has been reached. Signed-off-by: Ilkka Koskinen Tested-by: Tuomas Katila Acked-by: Tony Lindgren Signed-off-by: Grant Likely --- drivers/spi/omap2_mcspi.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index 27a3fa1f3bf4..2a651e61bfbf 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -296,6 +296,19 @@ static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi) return 0; } +static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(1000); + while (!(__raw_readl(reg) & bit)) { + if (time_after(jiffies, timeout)) + return -1; + cpu_relax(); + } + return 0; +} + static unsigned omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) { @@ -309,11 +322,14 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) u32 l; u8 * rx; const u8 * tx; + void __iomem *chstat_reg; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; l = mcspi_cached_chconf0(spi); + chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; + count = xfer->len; c = count; word_len = cs->word_len; @@ -382,6 +398,16 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) if (tx != NULL) { wait_for_completion(&mcspi_dma->dma_tx_completion); dma_unmap_single(NULL, xfer->tx_dma, count, DMA_TO_DEVICE); + + /* for TX_ONLY mode, be sure all words have shifted out */ + if (rx == NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS) < 0) + dev_err(&spi->dev, "TXS timed out\n"); + else if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_EOT) < 0) + dev_err(&spi->dev, "EOT timed out\n"); + } } if (rx != NULL) { @@ -435,19 +461,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) return count; } -static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(1000); - while (!(__raw_readl(reg) & bit)) { - if (time_after(jiffies, timeout)) - return -1; - cpu_relax(); - } - return 0; -} - static unsigned omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) { -- cgit v1.2.2 From 0c03a1dd5bd8a37932ae1d519156172affef22fd Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Thu, 22 Apr 2010 15:58:25 -0700 Subject: spi: add spi_tegra driver v2 changes: from Thierry Reding: * add "select TEGRA_SYSTEM_DMA" to Kconfig from Grant Likely: * add oneline description to header * inline references to DRIVER_NAME * inline references to BUSY_TIMEOUT * open coded bytes_per_word() * spi_readl/writel -> spi_tegra_readl/writel * move transfer validation to spi_tegra_transfer * don't request_mem_region iomem as platform bus does that for us * __exit -> __devexit v3 changes: from Russell King: * put request_mem_region back int from Grant Likely: * remove #undef DEBUG * add SLINK_ to register bit defines * remove unused bytes_per_word * make spi_tegra_readl/writel static linine * various refactoring for clarity * mark err if BSY bit is not cleared after 1000 retries * move spinlock to protect setting of RDY bit * subsys_initcall -> module_init v3 changes: from Grant Likely: * update spi_tegra to use PTR_ERRless dma API v4 changes: from Grant Likely: * remove empty spi_tegra_cleanup fucntion * allow device ids of -1 Signed-off-by: Erik Gilling Acked-by: Grant Likely Cc: Thierry Reding Cc: Russell King spi: tegra: cleanups from upstream review Change-Id: Icecf7e64efcb39de072a15234ba1faa4bad40d25 Signed-off-by: Erik Gilling --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi_tegra.c | 618 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 626 insertions(+) create mode 100644 drivers/spi/spi_tegra.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4b9eec68fad6..78f9fd02c1b2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -329,6 +329,13 @@ config SPI_STMP3XXX help SPI driver for Freescale STMP37xx/378x SoC SSP interface +config SPI_TEGRA + tristate "Nvidia Tegra SPI controller" + depends on ARCH_TEGRA + select TEGRA_SYSTEM_DMA + help + SPI driver for NVidia Tegra SoCs + config SPI_TOPCLIFF_PCH tristate "Topcliff PCH SPI Controller" depends on PCI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 557aaadf56b2..8bc1a5abac1f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o +obj-$(CONFIG_SPI_TEGRA) += spi_tegra.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o diff --git a/drivers/spi/spi_tegra.c b/drivers/spi/spi_tegra.c new file mode 100644 index 000000000000..0385fde202a6 --- /dev/null +++ b/drivers/spi/spi_tegra.c @@ -0,0 +1,618 @@ +/* + * Driver for Nvidia TEGRA spi controller. + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Erik Gilling + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define SLINK_COMMAND 0x000 +#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0) +#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5) +#define SLINK_BOTH_EN (1 << 10) +#define SLINK_CS_SW (1 << 11) +#define SLINK_CS_VALUE (1 << 12) +#define SLINK_CS_POLARITY (1 << 13) +#define SLINK_IDLE_SDA_DRIVE_LOW (0 << 16) +#define SLINK_IDLE_SDA_DRIVE_HIGH (1 << 16) +#define SLINK_IDLE_SDA_PULL_LOW (2 << 16) +#define SLINK_IDLE_SDA_PULL_HIGH (3 << 16) +#define SLINK_IDLE_SDA_MASK (3 << 16) +#define SLINK_CS_POLARITY1 (1 << 20) +#define SLINK_CK_SDA (1 << 21) +#define SLINK_CS_POLARITY2 (1 << 22) +#define SLINK_CS_POLARITY3 (1 << 23) +#define SLINK_IDLE_SCLK_DRIVE_LOW (0 << 24) +#define SLINK_IDLE_SCLK_DRIVE_HIGH (1 << 24) +#define SLINK_IDLE_SCLK_PULL_LOW (2 << 24) +#define SLINK_IDLE_SCLK_PULL_HIGH (3 << 24) +#define SLINK_IDLE_SCLK_MASK (3 << 24) +#define SLINK_M_S (1 << 28) +#define SLINK_WAIT (1 << 29) +#define SLINK_GO (1 << 30) +#define SLINK_ENB (1 << 31) + +#define SLINK_COMMAND2 0x004 +#define SLINK_LSBFE (1 << 0) +#define SLINK_SSOE (1 << 1) +#define SLINK_SPIE (1 << 4) +#define SLINK_BIDIROE (1 << 6) +#define SLINK_MODFEN (1 << 7) +#define SLINK_INT_SIZE(x) (((x) & 0x1f) << 8) +#define SLINK_CS_ACTIVE_BETWEEN (1 << 17) +#define SLINK_SS_EN_CS(x) (((x) & 0x3) << 18) +#define SLINK_SS_SETUP(x) (((x) & 0x3) << 20) +#define SLINK_FIFO_REFILLS_0 (0 << 22) +#define SLINK_FIFO_REFILLS_1 (1 << 22) +#define SLINK_FIFO_REFILLS_2 (2 << 22) +#define SLINK_FIFO_REFILLS_3 (3 << 22) +#define SLINK_FIFO_REFILLS_MASK (3 << 22) +#define SLINK_WAIT_PACK_INT(x) (((x) & 0x7) << 26) +#define SLINK_SPC0 (1 << 29) +#define SLINK_TXEN (1 << 30) +#define SLINK_RXEN (1 << 31) + +#define SLINK_STATUS 0x008 +#define SLINK_COUNT(val) (((val) >> 0) & 0x1f) +#define SLINK_WORD(val) (((val) >> 5) & 0x1f) +#define SLINK_BLK_CNT(val) (((val) >> 0) & 0xffff) +#define SLINK_MODF (1 << 16) +#define SLINK_RX_UNF (1 << 18) +#define SLINK_TX_OVF (1 << 19) +#define SLINK_TX_FULL (1 << 20) +#define SLINK_TX_EMPTY (1 << 21) +#define SLINK_RX_FULL (1 << 22) +#define SLINK_RX_EMPTY (1 << 23) +#define SLINK_TX_UNF (1 << 24) +#define SLINK_RX_OVF (1 << 25) +#define SLINK_TX_FLUSH (1 << 26) +#define SLINK_RX_FLUSH (1 << 27) +#define SLINK_SCLK (1 << 28) +#define SLINK_ERR (1 << 29) +#define SLINK_RDY (1 << 30) +#define SLINK_BSY (1 << 31) + +#define SLINK_MAS_DATA 0x010 +#define SLINK_SLAVE_DATA 0x014 + +#define SLINK_DMA_CTL 0x018 +#define SLINK_DMA_BLOCK_SIZE(x) (((x) & 0xffff) << 0) +#define SLINK_TX_TRIG_1 (0 << 16) +#define SLINK_TX_TRIG_4 (1 << 16) +#define SLINK_TX_TRIG_8 (2 << 16) +#define SLINK_TX_TRIG_16 (3 << 16) +#define SLINK_TX_TRIG_MASK (3 << 16) +#define SLINK_RX_TRIG_1 (0 << 18) +#define SLINK_RX_TRIG_4 (1 << 18) +#define SLINK_RX_TRIG_8 (2 << 18) +#define SLINK_RX_TRIG_16 (3 << 18) +#define SLINK_RX_TRIG_MASK (3 << 18) +#define SLINK_PACKED (1 << 20) +#define SLINK_PACK_SIZE_4 (0 << 21) +#define SLINK_PACK_SIZE_8 (1 << 21) +#define SLINK_PACK_SIZE_16 (2 << 21) +#define SLINK_PACK_SIZE_32 (3 << 21) +#define SLINK_PACK_SIZE_MASK (3 << 21) +#define SLINK_IE_TXC (1 << 26) +#define SLINK_IE_RXC (1 << 27) +#define SLINK_DMA_EN (1 << 31) + +#define SLINK_STATUS2 0x01c +#define SLINK_TX_FIFO_EMPTY_COUNT(val) (((val) & 0x3f) >> 0) +#define SLINK_RX_FIFO_FULL_COUNT(val) (((val) & 0x3f) >> 16) + +#define SLINK_TX_FIFO 0x100 +#define SLINK_RX_FIFO 0x180 + +static const unsigned long spi_tegra_req_sels[] = { + TEGRA_DMA_REQ_SEL_SL2B1, + TEGRA_DMA_REQ_SEL_SL2B2, + TEGRA_DMA_REQ_SEL_SL2B3, + TEGRA_DMA_REQ_SEL_SL2B4, +}; + +#define BB_LEN 32 + +struct spi_tegra_data { + struct spi_master *master; + struct platform_device *pdev; + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + unsigned long phys; + + u32 cur_speed; + + struct list_head queue; + struct spi_transfer *cur; + unsigned cur_pos; + unsigned cur_len; + unsigned cur_bytes_per_word; + + /* The tegra spi controller has a bug which causes the first word + * in PIO transactions to be garbage. Since packed DMA transactions + * require transfers to be 4 byte aligned we need a bounce buffer + * for the generic case. + */ + struct tegra_dma_req rx_dma_req; + struct tegra_dma_channel *rx_dma; + u32 *rx_bb; + dma_addr_t rx_bb_phys; +}; + + +static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi, + unsigned long reg) +{ + return readl(tspi->base + reg); +} + +static inline void spi_tegra_writel(struct spi_tegra_data *tspi, + unsigned long val, + unsigned long reg) +{ + writel(val, tspi->base + reg); +} + +static void spi_tegra_go(struct spi_tegra_data *tspi) +{ + unsigned long val; + + wmb(); + + val = spi_tegra_readl(tspi, SLINK_DMA_CTL); + val &= ~SLINK_DMA_BLOCK_SIZE(~0) & ~SLINK_DMA_EN; + val |= SLINK_DMA_BLOCK_SIZE(tspi->rx_dma_req.size / 4 - 1); + spi_tegra_writel(tspi, val, SLINK_DMA_CTL); + + tegra_dma_enqueue_req(tspi->rx_dma, &tspi->rx_dma_req); + + val |= SLINK_DMA_EN; + spi_tegra_writel(tspi, val, SLINK_DMA_CTL); +} + +static unsigned spi_tegra_fill_tx_fifo(struct spi_tegra_data *tspi, + struct spi_transfer *t) +{ + unsigned len = min(t->len - tspi->cur_pos, BB_LEN * + tspi->cur_bytes_per_word); + u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_pos; + int i, j; + unsigned long val; + + val = spi_tegra_readl(tspi, SLINK_COMMAND); + val &= ~SLINK_WORD_SIZE(~0); + val |= SLINK_WORD_SIZE(len / tspi->cur_bytes_per_word - 1); + spi_tegra_writel(tspi, val, SLINK_COMMAND); + + for (i = 0; i < len; i += tspi->cur_bytes_per_word) { + val = 0; + for (j = 0; j < tspi->cur_bytes_per_word; j++) + val |= tx_buf[i + j] << j * 8; + + spi_tegra_writel(tspi, val, SLINK_TX_FIFO); + } + + tspi->rx_dma_req.size = len / tspi->cur_bytes_per_word * 4; + + return len; +} + +static unsigned spi_tegra_drain_rx_fifo(struct spi_tegra_data *tspi, + struct spi_transfer *t) +{ + unsigned len = tspi->cur_len; + u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_pos; + int i, j; + unsigned long val; + + for (i = 0; i < len; i += tspi->cur_bytes_per_word) { + val = tspi->rx_bb[i / tspi->cur_bytes_per_word]; + for (j = 0; j < tspi->cur_bytes_per_word; j++) + rx_buf[i + j] = (val >> (j * 8)) & 0xff; + } + + return len; +} + +static void spi_tegra_start_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); + u32 speed; + u8 bits_per_word; + unsigned long val; + + speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; + bits_per_word = t->bits_per_word ? t->bits_per_word : + spi->bits_per_word; + + tspi->cur_bytes_per_word = (bits_per_word - 1) / 8 + 1; + + if (speed != tspi->cur_speed) + clk_set_rate(tspi->clk, speed); + + if (tspi->cur_speed == 0) + clk_enable(tspi->clk); + + tspi->cur_speed = speed; + + val = spi_tegra_readl(tspi, SLINK_COMMAND2); + val &= ~SLINK_SS_EN_CS(~0) | SLINK_RXEN | SLINK_TXEN; + if (t->rx_buf) + val |= SLINK_RXEN; + if (t->tx_buf) + val |= SLINK_TXEN; + val |= SLINK_SS_EN_CS(spi->chip_select); + val |= SLINK_SPIE; + spi_tegra_writel(tspi, val, SLINK_COMMAND2); + + val = spi_tegra_readl(tspi, SLINK_COMMAND); + val &= ~SLINK_BIT_LENGTH(~0); + val |= SLINK_BIT_LENGTH(bits_per_word - 1); + + /* FIXME: should probably control CS manually so that we can be sure + * it does not go low between transfer and to support delay_usecs + * correctly. + */ + val &= ~SLINK_IDLE_SCLK_MASK & ~SLINK_CK_SDA & ~SLINK_CS_SW; + + if (spi->mode & SPI_CPHA) + val |= SLINK_CK_SDA; + + if (spi->mode & SPI_CPOL) + val |= SLINK_IDLE_SCLK_DRIVE_HIGH; + else + val |= SLINK_IDLE_SCLK_DRIVE_LOW; + + val |= SLINK_M_S; + + spi_tegra_writel(tspi, val, SLINK_COMMAND); + + spi_tegra_writel(tspi, SLINK_RX_FLUSH | SLINK_TX_FLUSH, SLINK_STATUS); + + tspi->cur = t; + tspi->cur_pos = 0; + tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, t); + + spi_tegra_go(tspi); +} + +static void spi_tegra_start_message(struct spi_device *spi, + struct spi_message *m) +{ + struct spi_transfer *t; + + m->actual_length = 0; + m->status = 0; + + t = list_first_entry(&m->transfers, struct spi_transfer, transfer_list); + spi_tegra_start_transfer(spi, t); +} + +static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req) +{ + struct spi_tegra_data *tspi = req->dev; + unsigned long flags; + struct spi_message *m; + struct spi_device *spi; + int timeout = 0; + unsigned long val; + + /* the SPI controller may come back with both the BSY and RDY bits + * set. In this case we need to wait for the BSY bit to clear so + * that we are sure the DMA is finished. 1000 reads was empirically + * determined to be long enough. + */ + while (timeout++ < 1000) { + if (!(spi_tegra_readl(tspi, SLINK_STATUS) & SLINK_BSY)) + break; + } + + spin_lock_irqsave(&tspi->lock, flags); + + if (timeout >= 1000) + m->status = -EIO; + + val = spi_tegra_readl(tspi, SLINK_STATUS); + val |= SLINK_RDY; + spi_tegra_writel(tspi, val, SLINK_STATUS); + + + m = list_first_entry(&tspi->queue, struct spi_message, queue); + spi = m->state; + + tspi->cur_pos += spi_tegra_drain_rx_fifo(tspi, tspi->cur); + m->actual_length += tspi->cur_pos; + + if (tspi->cur_pos < tspi->cur->len) { + tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, tspi->cur); + spi_tegra_go(tspi); + } else if (!list_is_last(&tspi->cur->transfer_list, + &m->transfers)) { + tspi->cur = list_first_entry(&tspi->cur->transfer_list, + struct spi_transfer, + transfer_list); + spi_tegra_start_transfer(spi, tspi->cur); + } else { + list_del(&m->queue); + + m->complete(m->context); + + if (!list_empty(&tspi->queue)) { + m = list_first_entry(&tspi->queue, struct spi_message, + queue); + spi = m->state; + spi_tegra_start_message(spi, m); + } else { + clk_disable(tspi->clk); + tspi->cur_speed = 0; + } + } + + spin_unlock_irqrestore(&tspi->lock, flags); +} + +static int spi_tegra_setup(struct spi_device *spi) +{ + struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); + unsigned long cs_bit; + unsigned long val; + unsigned long flags; + + dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n", + spi->bits_per_word, + spi->mode & SPI_CPOL ? "" : "~", + spi->mode & SPI_CPHA ? "" : "~", + spi->max_speed_hz); + + + switch (spi->chip_select) { + case 0: + cs_bit = SLINK_CS_POLARITY; + break; + + case 1: + cs_bit = SLINK_CS_POLARITY1; + break; + + case 2: + cs_bit = SLINK_CS_POLARITY2; + break; + + case 4: + cs_bit = SLINK_CS_POLARITY3; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&tspi->lock, flags); + + val = spi_tegra_readl(tspi, SLINK_COMMAND); + if (spi->mode & SPI_CS_HIGH) + val |= cs_bit; + else + val &= ~cs_bit; + spi_tegra_writel(tspi, val, SLINK_COMMAND); + + spin_unlock_irqrestore(&tspi->lock, flags); + + return 0; +} + +static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); + struct spi_transfer *t; + unsigned long flags; + int was_empty; + + if (list_empty(&m->transfers) || !m->complete) + return -EINVAL; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->bits_per_word < 0 || t->bits_per_word > 32) + return -EINVAL; + + if (t->len == 0) + return -EINVAL; + + if (!t->rx_buf && !t->tx_buf) + return -EINVAL; + } + + m->state = spi; + + spin_lock_irqsave(&tspi->lock, flags); + was_empty = list_empty(&tspi->queue); + list_add_tail(&m->queue, &tspi->queue); + + if (was_empty) + spi_tegra_start_message(spi, m); + + spin_unlock_irqrestore(&tspi->lock, flags); + + return 0; +} + +static int __init spi_tegra_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct spi_tegra_data *tspi; + struct resource *r; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof *tspi); + if (master == NULL) { + dev_err(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + + master->bus_num = pdev->id; + + master->setup = spi_tegra_setup; + master->transfer = spi_tegra_transfer; + master->num_chipselect = 4; + + dev_set_drvdata(&pdev->dev, master); + tspi = spi_master_get_devdata(master); + tspi->master = master; + tspi->pdev = pdev; + spin_lock_init(&tspi->lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENODEV; + goto err0; + } + + if (!request_mem_region(r->start, (r->end - r->start) + 1, + dev_name(&pdev->dev))) { + ret = -EBUSY; + goto err0; + } + + tspi->phys = r->start; + tspi->base = ioremap(r->start, r->end - r->start + 1); + if (!tspi->base) { + dev_err(&pdev->dev, "can't ioremap iomem\n"); + ret = -ENOMEM; + goto err1; + } + + tspi->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR_OR_NULL(tspi->clk)) { + dev_err(&pdev->dev, "can not get clock\n"); + ret = PTR_ERR(tspi->clk); + goto err2; + } + + INIT_LIST_HEAD(&tspi->queue); + + tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); + if (!tspi->rx_dma) { + dev_err(&pdev->dev, "can not allocate rx dma channel\n"); + ret = -ENODEV; + goto err3; + } + + tspi->rx_bb = dma_alloc_coherent(&pdev->dev, sizeof(u32) * BB_LEN, + &tspi->rx_bb_phys, GFP_KERNEL); + if (!tspi->rx_bb) { + dev_err(&pdev->dev, "can not allocate rx bounce buffer\n"); + ret = -ENOMEM; + goto err4; + } + + tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete; + tspi->rx_dma_req.to_memory = 1; + tspi->rx_dma_req.dest_addr = tspi->rx_bb_phys; + tspi->rx_dma_req.dest_bus_width = 32; + tspi->rx_dma_req.source_addr = tspi->phys + SLINK_RX_FIFO; + tspi->rx_dma_req.source_bus_width = 32; + tspi->rx_dma_req.source_wrap = 4; + tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id]; + tspi->rx_dma_req.dev = tspi; + + ret = spi_register_master(master); + + if (ret < 0) + goto err5; + + return ret; + +err5: + dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN, + tspi->rx_bb, tspi->rx_bb_phys); +err4: + tegra_dma_free_channel(tspi->rx_dma); +err3: + clk_put(tspi->clk); +err2: + iounmap(tspi->base); +err1: + release_mem_region(r->start, (r->end - r->start) + 1); +err0: + spi_master_put(master); + return ret; +} + +static int __devexit spi_tegra_remove(struct platform_device *pdev) +{ + struct spi_master *master; + struct spi_tegra_data *tspi; + struct resource *r; + + master = dev_get_drvdata(&pdev->dev); + tspi = spi_master_get_devdata(master); + + tegra_dma_free_channel(tspi->rx_dma); + + dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN, + tspi->rx_bb, tspi->rx_bb_phys); + + clk_put(tspi->clk); + iounmap(tspi->base); + + spi_master_put(master); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(r->start, (r->end - r->start) + 1); + + return 0; +} + +MODULE_ALIAS("platform:spi_tegra"); + +static struct platform_driver spi_tegra_driver = { + .driver = { + .name = "spi_tegra", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(spi_tegra_remove), +}; + +static int __init spi_tegra_init(void) +{ + return platform_driver_probe(&spi_tegra_driver, spi_tegra_probe); +} +module_init(spi_tegra_init); + +static void __exit spi_tegra_exit(void) +{ + platform_driver_unregister(&spi_tegra_driver); +} +module_exit(spi_tegra_exit); + +MODULE_LICENSE("GPL"); -- cgit v1.2.2 From 2b9603a0d7e395fb844af90fba71448bc8019077 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Mon, 2 Aug 2010 15:52:15 +0800 Subject: spi: enable spi_board_info to be registered after spi_master Currently spi_register_board_info() has to be called before its related spi_master be registered, otherwise these board info will be just ignored. This patch will remove this order limit, it adds a global spi master list like the existing global board info listr. Whenever a board info or a spi_master is registered, the spi master list or board info list will be scanned, and a new spi device will be created if there is a master-board info match. Signed-off-by: Feng Tang Signed-off-by: Grant Likely --- drivers/spi/spi.c | 88 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 41 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b5a78a1f4421..0845cd4c5155 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -29,11 +29,6 @@ #include #include - -/* SPI bustype and spi_master class are registered after board init code - * provides the SPI device tables, ensuring that both are present by the - * time controller driver registration causes spi_devices to "enumerate". - */ static void spidev_release(struct device *dev) { struct spi_device *spi = to_spi_device(dev); @@ -202,11 +197,16 @@ EXPORT_SYMBOL_GPL(spi_register_driver); struct boardinfo { struct list_head list; - unsigned n_board_info; - struct spi_board_info board_info[0]; + struct spi_board_info board_info; }; static LIST_HEAD(board_list); +static LIST_HEAD(spi_master_list); + +/* + * Used to protect add/del opertion for board_info list and + * spi_master list, and their matching process + */ static DEFINE_MUTEX(board_lock); /** @@ -371,6 +371,20 @@ struct spi_device *spi_new_device(struct spi_master *master, } EXPORT_SYMBOL_GPL(spi_new_device); +static void spi_match_master_to_boardinfo(struct spi_master *master, + struct spi_board_info *bi) +{ + struct spi_device *dev; + + if (master->bus_num != bi->bus_num) + return; + + dev = spi_new_device(master, bi); + if (!dev) + dev_err(master->dev.parent, "can't create new device for %s\n", + bi->modalias); +} + /** * spi_register_board_info - register SPI devices for a given board * @info: array of chip descriptors @@ -393,43 +407,25 @@ EXPORT_SYMBOL_GPL(spi_new_device); int __init spi_register_board_info(struct spi_board_info const *info, unsigned n) { - struct boardinfo *bi; + struct boardinfo *bi; + int i; - bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL); + bi = kzalloc(n * sizeof(*bi), GFP_KERNEL); if (!bi) return -ENOMEM; - bi->n_board_info = n; - memcpy(bi->board_info, info, n * sizeof *info); - mutex_lock(&board_lock); - list_add_tail(&bi->list, &board_list); - mutex_unlock(&board_lock); - return 0; -} + for (i = 0; i < n; i++, bi++, info++) { + struct spi_master *master; -/* FIXME someone should add support for a __setup("spi", ...) that - * creates board info from kernel command lines - */ - -static void scan_boardinfo(struct spi_master *master) -{ - struct boardinfo *bi; - - mutex_lock(&board_lock); - list_for_each_entry(bi, &board_list, list) { - struct spi_board_info *chip = bi->board_info; - unsigned n; - - for (n = bi->n_board_info; n > 0; n--, chip++) { - if (chip->bus_num != master->bus_num) - continue; - /* NOTE: this relies on spi_new_device to - * issue diagnostics when given bogus inputs - */ - (void) spi_new_device(master, chip); - } + memcpy(&bi->board_info, info, sizeof(*info)); + mutex_lock(&board_lock); + list_add_tail(&bi->list, &board_list); + list_for_each_entry(master, &spi_master_list, list) + spi_match_master_to_boardinfo(master, &bi->board_info); + mutex_unlock(&board_lock); } - mutex_unlock(&board_lock); + + return 0; } /*-------------------------------------------------------------------------*/ @@ -512,6 +508,7 @@ int spi_register_master(struct spi_master *master) { static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); struct device *dev = master->dev.parent; + struct boardinfo *bi; int status = -ENODEV; int dynamic = 0; @@ -547,8 +544,12 @@ int spi_register_master(struct spi_master *master) dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); - /* populate children from any spi device tables */ - scan_boardinfo(master); + mutex_lock(&board_lock); + list_add_tail(&master->list, &spi_master_list); + list_for_each_entry(bi, &board_list, list) + spi_match_master_to_boardinfo(master, &bi->board_info); + mutex_unlock(&board_lock); + status = 0; /* Register devices from the device tree */ @@ -579,7 +580,12 @@ void spi_unregister_master(struct spi_master *master) { int dummy; - dummy = device_for_each_child(&master->dev, NULL, __unregister); + mutex_lock(&board_lock); + list_del(&master->list); + mutex_unlock(&board_lock); + + dummy = device_for_each_child(master->dev.parent, &master->dev, + __unregister); device_unregister(&master->dev); } EXPORT_SYMBOL_GPL(spi_unregister_master); -- cgit v1.2.2 From 782a895693a52fd8b288c33bdd7e98357888fc1f Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 22 Oct 2010 02:01:48 -0400 Subject: spi/bfin_spi: handle error/status changes after data interrupts The error interrupt on the BF537 SIC cannot be enabled on a per-peripheral basis. Once the error interrupt is enabled for one peripheral, it is automatically enabled for all. So in the Blackfin on-chip SPI driver, we need to clear out these known errors in the data interrupt once we've successfully finished processing all of the pending data. Signed-off-by: Michael Hennerich Signed-off-by: Mike Frysinger Signed-off-by: Grant Likely --- drivers/spi/spi_bfin5xx.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index ab483a0ec6d0..d3da41d3f92d 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -504,6 +504,15 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) "in dma_irq_handler dmastat:0x%x spistat:0x%x\n", dmastat, spistat); + if (drv_data->rx != NULL) { + u16 cr = read_CTRL(drv_data); + /* discard old RX data and clear RXS */ + bfin_spi_dummy_read(drv_data); + write_CTRL(drv_data, cr & ~BIT_CTL_ENABLE); /* Disable SPI */ + write_CTRL(drv_data, cr & ~BIT_CTL_TIMOD); /* Restore State */ + write_STAT(drv_data, BIT_STAT_CLR); /* Clear Status */ + } + clear_dma_irqstat(drv_data->dma_channel); /* -- cgit v1.2.2 From 73e1ac16258fe7e55cce4691e32c0b44d4579e08 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 22 Oct 2010 02:01:47 -0400 Subject: spi/bfin_spi: only request GPIO on first load The gpiolib code does not allow people to do gpio_request() on a GPIO once it has already been requested. So make sure we only request the pin on the first setup of a SPI device. Otherwise, if you attempts to reconfigure a SPI device on the fly (like change bit sizes), the setup function incorrectly fails. Signed-off-by: Michael Hennerich Signed-off-by: Mike Frysinger Signed-off-by: Grant Likely --- drivers/spi/spi_bfin5xx.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index d3da41d3f92d..3f223511127b 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -1108,12 +1108,15 @@ static int bfin_spi_setup(struct spi_device *spi) } if (chip->chip_select_num >= MAX_CTRL_CS) { - ret = gpio_request(chip->cs_gpio, spi->modalias); - if (ret) { - dev_err(&spi->dev, "gpio_request() error\n"); - goto pin_error; + /* Only request on first setup */ + if (spi_get_ctldata(spi) == NULL) { + ret = gpio_request(chip->cs_gpio, spi->modalias); + if (ret) { + dev_err(&spi->dev, "gpio_request() error\n"); + goto pin_error; + } + gpio_direction_output(chip->cs_gpio, 1); } - gpio_direction_output(chip->cs_gpio, 1); } dev_dbg(&spi->dev, "setup spi chip %s, width is %d, dma is %d\n", -- cgit v1.2.2 From eb288a1f45e2aa903ac8edf67dc6d59df0369fe1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 21 Oct 2010 21:06:44 +0200 Subject: spi: fixed odd static string conventions in core code This patch removes convention of passing a static string as a parameter to another static string. The convention is intended to reduce text usage by sharing the common bits of the string, but the implementation is inherently fragile (a change to one format string but not the other will nullify any possible advantage), it isn't necessarily a net win depending on what this compiler does, and it it reduces code readability. Signed-off-by: Linus Walleij [grant.likely@secretlab.ca: removed dev_dbg->dev_err hunk] Signed-off-by: Grant Likely --- drivers/spi/spi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 0845cd4c5155..709c836607de 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -300,16 +300,16 @@ int spi_add_device(struct spi_device *spi) */ status = spi_setup(spi); if (status < 0) { - dev_err(dev, "can't %s %s, status %d\n", - "setup", dev_name(&spi->dev), status); + dev_err(dev, "can't setup %s, status %d\n", + dev_name(&spi->dev), status); goto done; } /* Device may be bound to an active driver when this returns */ status = device_add(&spi->dev); if (status < 0) - dev_err(dev, "can't %s %s, status %d\n", - "add", dev_name(&spi->dev), status); + dev_err(dev, "can't add %s, status %d\n", + dev_name(&spi->dev), status); else dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev)); @@ -658,7 +658,7 @@ int spi_setup(struct spi_device *spi) */ bad_bits = spi->mode & ~spi->master->mode_bits; if (bad_bits) { - dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", + dev_err(&spi->dev, "setup: unsupported mode bits %x\n", bad_bits); return -EINVAL; } -- cgit v1.2.2 From f41649e040b1f63e809fb2adb78ed2f3c3ebbcd0 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Wed, 8 Sep 2010 18:16:45 -0700 Subject: spi: tegra: fix error setting on timeout avoids derefencing an uninitialized pointer Change-Id: Icf528441ae481e9f6f5ddc0be32c7c217fa49701 Signed-off-by: Erik Gilling --- drivers/spi/spi_tegra.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_tegra.c b/drivers/spi/spi_tegra.c index 0385fde202a6..bb7df02a5472 100644 --- a/drivers/spi/spi_tegra.c +++ b/drivers/spi/spi_tegra.c @@ -338,15 +338,15 @@ static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req) spin_lock_irqsave(&tspi->lock, flags); - if (timeout >= 1000) - m->status = -EIO; - val = spi_tegra_readl(tspi, SLINK_STATUS); val |= SLINK_RDY; spi_tegra_writel(tspi, val, SLINK_STATUS); - m = list_first_entry(&tspi->queue, struct spi_message, queue); + + if (timeout >= 1000) + m->status = -EIO; + spi = m->state; tspi->cur_pos += spi_tegra_drain_rx_fifo(tspi, tspi->cur); -- cgit v1.2.2 From b595076a180a56d1bb170e6eceda6eb9d76f4cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 1 Nov 2010 15:38:34 -0400 Subject: tree-wide: fix comment/printk typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "gadget", "through", "command", "maintain", "maintain", "controller", "address", "between", "initiali[zs]e", "instead", "function", "select", "already", "equal", "access", "management", "hierarchy", "registration", "interest", "relative", "memory", "offset", "already", Signed-off-by: Uwe Kleine-König Signed-off-by: Jiri Kosina --- drivers/spi/atmel_spi.c | 4 ++-- drivers/spi/spidev.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 154529aacc03..49596a18f953 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -341,9 +341,9 @@ static void atmel_spi_next_message(struct spi_master *master) /* * For DMA, tx_buf/tx_dma have the same relationship as rx_buf/rx_dma: * - The buffer is either valid for CPU access, else NULL - * - If the buffer is valid, so is its DMA addresss + * - If the buffer is valid, so is its DMA address * - * This driver manages the dma addresss unless message->is_dma_mapped. + * This driver manages the dma address unless message->is_dma_mapped. */ static int atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 4e6245e67995..603428213d21 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -38,7 +38,7 @@ /* - * This supports acccess to SPI devices using normal userspace I/O calls. + * This supports access to SPI devices using normal userspace I/O calls. * Note that while traditional UNIX/POSIX I/O semantics are half duplex, * and often mask message boundaries, full SPI support requires full duplex * transfers. There are several kinds of internal message boundaries to -- cgit v1.2.2 From 91565c4068042b3d8e37e64e393ca105476419bd Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 14 Oct 2010 08:54:55 -0600 Subject: spi/xilinx: Eliminate pdata references from common code. The current code has the OF binding modifying the platform_data pointer which it must not do, and the common code doesn't really need to use a pdata pointer. This patch eliminates the platform_data references from the common part of the driver in preparation for merging the OF and non-OF versions. Signed-off-by: Grant Likely Tested-by: Michal Simek --- drivers/spi/xilinx_spi.c | 14 ++++---------- drivers/spi/xilinx_spi.h | 2 +- drivers/spi/xilinx_spi_of.c | 17 ++++------------- drivers/spi/xilinx_spi_pltfm.c | 4 +++- 4 files changed, 12 insertions(+), 25 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 80f2db5bcfd6..efb28ba4a4ec 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -351,18 +351,12 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) } struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, - u32 irq, s16 bus_num) + u32 irq, s16 bus_num, int num_cs, int little_endian, int bits_per_word) { struct spi_master *master; struct xilinx_spi *xspi; - struct xspi_platform_data *pdata = dev->platform_data; int ret; - if (!pdata) { - dev_err(dev, "No platform data attached\n"); - return NULL; - } - master = spi_alloc_master(dev, sizeof(struct xilinx_spi)); if (!master) return NULL; @@ -389,21 +383,21 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, } master->bus_num = bus_num; - master->num_chipselect = pdata->num_chipselect; + master->num_chipselect = num_cs; #ifdef CONFIG_OF master->dev.of_node = dev->of_node; #endif xspi->mem = *mem; xspi->irq = irq; - if (pdata->little_endian) { + if (little_endian) { xspi->read_fn = xspi_read32; xspi->write_fn = xspi_write32; } else { xspi->read_fn = xspi_read32_be; xspi->write_fn = xspi_write32_be; } - xspi->bits_per_word = pdata->bits_per_word; + xspi->bits_per_word = bits_per_word; if (xspi->bits_per_word == 8) { xspi->tx_fn = xspi_tx8; xspi->rx_fn = xspi_rx8; diff --git a/drivers/spi/xilinx_spi.h b/drivers/spi/xilinx_spi.h index d211accf68d2..d710a33f569f 100644 --- a/drivers/spi/xilinx_spi.h +++ b/drivers/spi/xilinx_spi.h @@ -26,7 +26,7 @@ #define XILINX_SPI_NAME "xilinx_spi" struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, - u32 irq, s16 bus_num); + u32 irq, s16 bus_num, int num_cs, int little_endian, int bits_per_word); void xilinx_spi_deinit(struct spi_master *master); #endif diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c index b66c2dbf20a5..c2d8ade87a38 100644 --- a/drivers/spi/xilinx_spi_of.c +++ b/drivers/spi/xilinx_spi_of.c @@ -42,12 +42,11 @@ static int __devinit xilinx_spi_of_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct spi_master *master; - struct xspi_platform_data *pdata; struct resource r_mem; struct resource r_irq; int rc = 0; const u32 *prop; - int len; + int len, num_cs; rc = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem); if (rc) { @@ -61,21 +60,15 @@ static int __devinit xilinx_spi_of_probe(struct platform_device *ofdev, return -ENODEV; } - ofdev->dev.platform_data = - kzalloc(sizeof(struct xspi_platform_data), GFP_KERNEL); - pdata = ofdev->dev.platform_data; - if (!pdata) - return -ENOMEM; - /* number of slave select bits is required */ prop = of_get_property(ofdev->dev.of_node, "xlnx,num-ss-bits", &len); if (!prop || len < sizeof(*prop)) { dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); return -EINVAL; } - pdata->num_chipselect = *prop; - pdata->bits_per_word = 8; - master = xilinx_spi_init(&ofdev->dev, &r_mem, r_irq.start, -1); + num_cs = __be32_to_cpup(prop); + master = xilinx_spi_init(&ofdev->dev, &r_mem, r_irq.start, -1, + num_cs, 0, 8); if (!master) return -ENODEV; @@ -88,8 +81,6 @@ static int __devexit xilinx_spi_remove(struct platform_device *ofdev) { xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev)); dev_set_drvdata(&ofdev->dev, 0); - kfree(ofdev->dev.platform_data); - ofdev->dev.platform_data = NULL; return 0; } diff --git a/drivers/spi/xilinx_spi_pltfm.c b/drivers/spi/xilinx_spi_pltfm.c index 24debac646a9..a16722acafea 100644 --- a/drivers/spi/xilinx_spi_pltfm.c +++ b/drivers/spi/xilinx_spi_pltfm.c @@ -54,7 +54,9 @@ static int __devinit xilinx_spi_probe(struct platform_device *dev) if (irq < 0) return -ENXIO; - master = xilinx_spi_init(&dev->dev, r, irq, dev->id); + master = xilinx_spi_init(&dev->dev, r, irq, dev->id, + pdata->num_chipselect, pdata->little_endian, + pdata->bits_per_word); if (!master) return -ENODEV; -- cgit v1.2.2 From 8fd8821b62397f8ddb7bfb23c3246a22770ab2ee Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 14 Oct 2010 09:04:29 -0600 Subject: spi/xilinx: fold platform_driver support into main body This patch merges the platform driver support into the main body of xilinx_spi.c in preparation for merging the OF and non-OF support code. Signed-off-by: Grant Likely Tested-by: Michal Simek --- drivers/spi/Kconfig | 7 --- drivers/spi/Makefile | 1 - drivers/spi/xilinx_spi.c | 79 ++++++++++++++++++++++++++++--- drivers/spi/xilinx_spi_pltfm.c | 104 ----------------------------------------- 4 files changed, 73 insertions(+), 118 deletions(-) delete mode 100644 drivers/spi/xilinx_spi_pltfm.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 78f9fd02c1b2..58ad21c700d3 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -368,13 +368,6 @@ config SPI_XILINX_OF help This is the OF driver for the SPI controller IP from the Xilinx EDK. -config SPI_XILINX_PLTFM - tristate "Xilinx SPI controller platform device" - depends on SPI_XILINX - help - This is the platform driver for the SPI controller IP - from the Xilinx EDK. - config SPI_NUC900 tristate "Nuvoton NUC900 series SPI" depends on ARCH_W90X900 && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 8bc1a5abac1f..0d03159b5668 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -44,7 +44,6 @@ obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o -obj-$(CONFIG_SPI_XILINX_PLTFM) += xilinx_spi_pltfm.o obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index efb28ba4a4ec..bb3b520df9dd 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -1,20 +1,22 @@ /* - * xilinx_spi.c - * * Xilinx SPI controller driver (master mode only) * * Author: MontaVista Software, Inc. * source@mvista.com * - * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the - * terms of the GNU General Public License version 2. This program is licensed - * "as is" without any warranty of any kind, whether express or implied. + * Copyright (c) 2010 Secret Lab Technologies, Ltd. + * Copyright (c) 2009 Intel Corporation + * 2002-2007 (c) MontaVista Software, Inc. + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. */ #include #include #include - +#include #include #include #include @@ -456,6 +458,71 @@ void xilinx_spi_deinit(struct spi_master *master) } EXPORT_SYMBOL(xilinx_spi_deinit); +static int __devinit xilinx_spi_probe(struct platform_device *dev) +{ + struct xspi_platform_data *pdata; + struct resource *r; + int irq; + struct spi_master *master; + u8 i; + + pdata = dev->dev.platform_data; + if (!pdata) + return -ENODEV; + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!r) + return -ENODEV; + + irq = platform_get_irq(dev, 0); + if (irq < 0) + return -ENXIO; + + master = xilinx_spi_init(&dev->dev, r, irq, dev->id, + pdata->num_chipselect, pdata->little_endian, + pdata->bits_per_word); + if (!master) + return -ENODEV; + + for (i = 0; i < pdata->num_devices; i++) + spi_new_device(master, pdata->devices + i); + + platform_set_drvdata(dev, master); + return 0; +} + +static int __devexit xilinx_spi_remove(struct platform_device *dev) +{ + xilinx_spi_deinit(platform_get_drvdata(dev)); + platform_set_drvdata(dev, 0); + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:" XILINX_SPI_NAME); + +static struct platform_driver xilinx_spi_driver = { + .probe = xilinx_spi_probe, + .remove = __devexit_p(xilinx_spi_remove), + .driver = { + .name = XILINX_SPI_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init xilinx_spi_pltfm_init(void) +{ + return platform_driver_register(&xilinx_spi_driver); +} +module_init(xilinx_spi_pltfm_init); + +static void __exit xilinx_spi_pltfm_exit(void) +{ + platform_driver_unregister(&xilinx_spi_driver); +} +module_exit(xilinx_spi_pltfm_exit); + MODULE_AUTHOR("MontaVista Software, Inc. "); MODULE_DESCRIPTION("Xilinx SPI driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/spi/xilinx_spi_pltfm.c b/drivers/spi/xilinx_spi_pltfm.c deleted file mode 100644 index a16722acafea..000000000000 --- a/drivers/spi/xilinx_spi_pltfm.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Support for Xilinx SPI platform devices - * Copyright (c) 2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Supports: - * Xilinx SPI devices as platform devices - * - * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "xilinx_spi.h" - -static int __devinit xilinx_spi_probe(struct platform_device *dev) -{ - struct xspi_platform_data *pdata; - struct resource *r; - int irq; - struct spi_master *master; - u8 i; - - pdata = dev->dev.platform_data; - if (!pdata) - return -ENODEV; - - r = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!r) - return -ENODEV; - - irq = platform_get_irq(dev, 0); - if (irq < 0) - return -ENXIO; - - master = xilinx_spi_init(&dev->dev, r, irq, dev->id, - pdata->num_chipselect, pdata->little_endian, - pdata->bits_per_word); - if (!master) - return -ENODEV; - - for (i = 0; i < pdata->num_devices; i++) - spi_new_device(master, pdata->devices + i); - - platform_set_drvdata(dev, master); - return 0; -} - -static int __devexit xilinx_spi_remove(struct platform_device *dev) -{ - xilinx_spi_deinit(platform_get_drvdata(dev)); - platform_set_drvdata(dev, 0); - - return 0; -} - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:" XILINX_SPI_NAME); - -static struct platform_driver xilinx_spi_driver = { - .probe = xilinx_spi_probe, - .remove = __devexit_p(xilinx_spi_remove), - .driver = { - .name = XILINX_SPI_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __init xilinx_spi_pltfm_init(void) -{ - return platform_driver_register(&xilinx_spi_driver); -} -module_init(xilinx_spi_pltfm_init); - -static void __exit xilinx_spi_pltfm_exit(void) -{ - platform_driver_unregister(&xilinx_spi_driver); -} -module_exit(xilinx_spi_pltfm_exit); - -MODULE_AUTHOR("Mocean Laboratories "); -MODULE_DESCRIPTION("Xilinx SPI platform driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.2 From eae6cb31d890e2860f9ce1b8ba73c27b6005af68 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 14 Oct 2010 09:32:53 -0600 Subject: spi/xilinx: merge OF support code into main driver Now that the of_platform_bus_type has been merged with the platform bus type, a single platform driver can handle both OF and non-OF use cases. This patch merges the OF support into the platform driver. Signed-off-by: Grant Likely Tested-by: Michal Simek --- drivers/spi/Kconfig | 7 --- drivers/spi/Makefile | 1 - drivers/spi/xilinx_spi.c | 56 ++++++++++++++++---- drivers/spi/xilinx_spi.h | 32 ------------ drivers/spi/xilinx_spi_of.c | 124 -------------------------------------------- 5 files changed, 45 insertions(+), 175 deletions(-) delete mode 100644 drivers/spi/xilinx_spi.h delete mode 100644 drivers/spi/xilinx_spi_of.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 58ad21c700d3..665d03d4e022 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -353,7 +353,6 @@ config SPI_XILINX tristate "Xilinx SPI controller common module" depends on HAS_IOMEM && EXPERIMENTAL select SPI_BITBANG - select SPI_XILINX_OF if (XILINX_VIRTEX || MICROBLAZE) help This exposes the SPI controller IP from the Xilinx EDK. @@ -362,12 +361,6 @@ config SPI_XILINX Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)" -config SPI_XILINX_OF - tristate "Xilinx SPI controller OF device" - depends on SPI_XILINX && (XILINX_VIRTEX || MICROBLAZE) - help - This is the OF driver for the SPI controller IP from the Xilinx EDK. - config SPI_NUC900 tristate "Nuvoton NUC900 series SPI" depends on ARCH_W90X900 && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 0d03159b5668..02dad4ae412d 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -43,7 +43,6 @@ obj-$(CONFIG_SPI_TEGRA) += spi_tegra.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o -obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index bb3b520df9dd..7adaef62a991 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -16,13 +16,12 @@ #include #include #include +#include #include #include #include -#include - -#include "xilinx_spi.h" #include +#include #define XILINX_SPI_NAME "xilinx_spi" @@ -352,6 +351,15 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_OF +static const struct of_device_id xilinx_spi_of_match[] = { + { .compatible = "xlnx,xps-spi-2.00.a", }, + { .compatible = "xlnx,xps-spi-2.00.b", }, + {} +}; +MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); +#endif + struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, u32 irq, s16 bus_num, int num_cs, int little_endian, int bits_per_word) { @@ -462,13 +470,35 @@ static int __devinit xilinx_spi_probe(struct platform_device *dev) { struct xspi_platform_data *pdata; struct resource *r; - int irq; + int irq, num_cs = 0, little_endian = 0, bits_per_word = 8; struct spi_master *master; u8 i; pdata = dev->dev.platform_data; - if (!pdata) - return -ENODEV; + if (pdata) { + num_cs = pdata->num_chipselect; + little_endian = pdata->little_endian; + bits_per_word = pdata->bits_per_word; + } + +#ifdef CONFIG_OF + if (dev->dev.of_node) { + const __be32 *prop; + int len; + + /* number of slave select bits is required */ + prop = of_get_property(dev->dev.of_node, "xlnx,num-ss-bits", + &len); + if (prop && len >= sizeof(*prop)) + num_cs = __be32_to_cpup(prop); + } +#endif + + if (!num_cs) { + dev_err(&dev->dev, "Missing slave select configuration data\n"); + return -EINVAL; + } + r = platform_get_resource(dev, IORESOURCE_MEM, 0); if (!r) @@ -478,14 +508,15 @@ static int __devinit xilinx_spi_probe(struct platform_device *dev) if (irq < 0) return -ENXIO; - master = xilinx_spi_init(&dev->dev, r, irq, dev->id, - pdata->num_chipselect, pdata->little_endian, - pdata->bits_per_word); + master = xilinx_spi_init(&dev->dev, r, irq, dev->id, num_cs, + little_endian, bits_per_word); if (!master) return -ENODEV; - for (i = 0; i < pdata->num_devices; i++) - spi_new_device(master, pdata->devices + i); + if (pdata) { + for (i = 0; i < pdata->num_devices; i++) + spi_new_device(master, pdata->devices + i); + } platform_set_drvdata(dev, master); return 0; @@ -508,6 +539,9 @@ static struct platform_driver xilinx_spi_driver = { .driver = { .name = XILINX_SPI_NAME, .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = xilinx_spi_of_match, +#endif }, }; diff --git a/drivers/spi/xilinx_spi.h b/drivers/spi/xilinx_spi.h deleted file mode 100644 index d710a33f569f..000000000000 --- a/drivers/spi/xilinx_spi.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Xilinx SPI device driver API and platform data header file - * - * Copyright (c) 2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _XILINX_SPI_H_ -#define _XILINX_SPI_H_ - -#include -#include - -#define XILINX_SPI_NAME "xilinx_spi" - -struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, - u32 irq, s16 bus_num, int num_cs, int little_endian, int bits_per_word); - -void xilinx_spi_deinit(struct spi_master *master); -#endif diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c deleted file mode 100644 index c2d8ade87a38..000000000000 --- a/drivers/spi/xilinx_spi_of.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Xilinx SPI OF device driver - * - * Copyright (c) 2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Supports: - * Xilinx SPI devices as OF devices - * - * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include "xilinx_spi.h" - - -static int __devinit xilinx_spi_of_probe(struct platform_device *ofdev, - const struct of_device_id *match) -{ - struct spi_master *master; - struct resource r_mem; - struct resource r_irq; - int rc = 0; - const u32 *prop; - int len, num_cs; - - rc = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem); - if (rc) { - dev_warn(&ofdev->dev, "invalid address\n"); - return rc; - } - - rc = of_irq_to_resource(ofdev->dev.of_node, 0, &r_irq); - if (rc == NO_IRQ) { - dev_warn(&ofdev->dev, "no IRQ found\n"); - return -ENODEV; - } - - /* number of slave select bits is required */ - prop = of_get_property(ofdev->dev.of_node, "xlnx,num-ss-bits", &len); - if (!prop || len < sizeof(*prop)) { - dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); - return -EINVAL; - } - num_cs = __be32_to_cpup(prop); - master = xilinx_spi_init(&ofdev->dev, &r_mem, r_irq.start, -1, - num_cs, 0, 8); - if (!master) - return -ENODEV; - - dev_set_drvdata(&ofdev->dev, master); - - return 0; -} - -static int __devexit xilinx_spi_remove(struct platform_device *ofdev) -{ - xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev)); - dev_set_drvdata(&ofdev->dev, 0); - return 0; -} - -static int __exit xilinx_spi_of_remove(struct platform_device *op) -{ - return xilinx_spi_remove(op); -} - -static const struct of_device_id xilinx_spi_of_match[] = { - { .compatible = "xlnx,xps-spi-2.00.a", }, - { .compatible = "xlnx,xps-spi-2.00.b", }, - {} -}; - -MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); - -static struct of_platform_driver xilinx_spi_of_driver = { - .probe = xilinx_spi_of_probe, - .remove = __exit_p(xilinx_spi_of_remove), - .driver = { - .name = "xilinx-xps-spi", - .owner = THIS_MODULE, - .of_match_table = xilinx_spi_of_match, - }, -}; - -static int __init xilinx_spi_of_init(void) -{ - return of_register_platform_driver(&xilinx_spi_of_driver); -} -module_init(xilinx_spi_of_init); - -static void __exit xilinx_spi_of_exit(void) -{ - of_unregister_platform_driver(&xilinx_spi_of_driver); -} -module_exit(xilinx_spi_of_exit); - -MODULE_AUTHOR("Mocean Laboratories "); -MODULE_DESCRIPTION("Xilinx SPI platform driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.2 From bc3f67a3e1b20756d4bfa5886a6b8fd0c068e6a4 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 14 Nov 2010 19:04:47 -0800 Subject: drivers/spi: Remove unnecessary semicolons Signed-off-by: Joe Perches Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 2 +- drivers/spi/spi_nuc900.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index fb3d1b31772d..2e506319b60f 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -956,7 +956,7 @@ static int configure_dma(struct pl022 *pl022) tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; break; case WRITING_U32: - tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;; + tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; break; } diff --git a/drivers/spi/spi_nuc900.c b/drivers/spi/spi_nuc900.c index dff63be0d0a8..d5be18b3078c 100644 --- a/drivers/spi/spi_nuc900.c +++ b/drivers/spi/spi_nuc900.c @@ -449,7 +449,7 @@ err_iomap: release_mem_region(hw->res->start, resource_size(hw->res)); kfree(hw->ioarea); err_pdata: - spi_master_put(hw->master);; + spi_master_put(hw->master); err_nomem: return err; -- cgit v1.2.2 From 3b740b10e9424f7ffb9baab1e6a3ff361cbf8ce3 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 3 Sep 2010 14:50:07 +0530 Subject: spi: davinci: fix checkpatch errors Fix the following checkpatch error: WARNING: unnecessary whitespace before a quoted newline + dev_info(&pdev->dev, "Controller at 0x%p \n", davinci_spi->base); Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index b85090caf7cf..6b0476225217 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -1184,7 +1184,7 @@ static int davinci_spi_probe(struct platform_device *pdev) if (ret) goto free_clk; - dev_info(&pdev->dev, "Controller at 0x%p \n", davinci_spi->base); + dev_info(&pdev->dev, "Controller at 0x%p\n", davinci_spi->base); if (!pdata->poll_mode) dev_info(&pdev->dev, "Operating in interrupt mode" -- cgit v1.2.2 From 778e261ed678c3654386fc38bc6c50353ce04cc7 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 3 Sep 2010 15:15:06 +0530 Subject: spi: davinci: whitespace cleanup Cleanup unnecessary white space from various parts of the file. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 6b0476225217..2fa5bec807ce 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -175,7 +175,7 @@ struct davinci_spi { u8 *tmp_buf; int count; struct davinci_spi_dma *dma_channels; - struct davinci_spi_platform_data *pdata; + struct davinci_spi_platform_data *pdata; void (*get_rx)(u32 rx_data, struct davinci_spi *); u32 (*get_tx)(struct davinci_spi *); @@ -435,7 +435,6 @@ static int davinci_spi_request_dma(struct spi_device *spi) * * This functions sets the default transfer method. */ - static int davinci_spi_setup(struct spi_device *spi) { int retval; @@ -1096,7 +1095,6 @@ static int davinci_spi_probe(struct platform_device *pdev) } clk_enable(davinci_spi->clk); - master->bus_num = pdev->id; master->num_chipselect = pdata->num_chipselect; master->setup = davinci_spi_setup; @@ -1113,15 +1111,15 @@ static int davinci_spi_probe(struct platform_device *pdev) davinci_spi->bitbang.flags |= SPI_READY; if (use_dma) { - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (r) - dma_rx_chan = r->start; - r = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (r) - dma_tx_chan = r->start; - r = platform_get_resource(pdev, IORESOURCE_DMA, 2); - if (r) - dma_eventq = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r) + dma_rx_chan = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (r) + dma_tx_chan = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 2); + if (r) + dma_eventq = r->start; } if (!use_dma || -- cgit v1.2.2 From c3c475c2353f6d13a5c77c3b6203c0240339da7d Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 3 Sep 2010 16:19:09 +0530 Subject: spi: davinci: remove unused variable 'pdata' The 'pdata' variable is unused in couple of routines. Remove such occurences. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 2fa5bec807ce..a92f507eaff7 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -299,12 +299,10 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, { struct davinci_spi *davinci_spi; - struct davinci_spi_platform_data *pdata; u8 bits_per_word = 0; u32 hz = 0, prescale = 0, clkspeed; davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; if (t) { bits_per_word = t->bits_per_word; @@ -357,11 +355,9 @@ static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) struct spi_device *spi = (struct spi_device *)data; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; - struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]); - pdata = davinci_spi->pdata; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_rx_channel); @@ -378,11 +374,9 @@ static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) struct spi_device *spi = (struct spi_device *)data; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; - struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]); - pdata = davinci_spi->pdata; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_tx_channel); @@ -398,13 +392,11 @@ static int davinci_spi_request_dma(struct spi_device *spi) { struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; - struct davinci_spi_platform_data *pdata; struct device *sdev; int r; davinci_spi = spi_master_get_devdata(spi->master); davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; - pdata = davinci_spi->pdata; sdev = davinci_spi->bitbang.master->dev.parent; r = edma_alloc_channel(davinci_spi_dma->dma_rx_sync_dev, -- cgit v1.2.2 From 8e206f1cbd0d1387bf7d5e463ca880b43458e2ea Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Fri, 20 Aug 2010 16:20:49 +0530 Subject: spi: davinci: removed unused #defines Remove unused defines from code which should help in easy reading of code. Also, use the opportuinity to keep the SPIGCR1 register defines together. Tested-By: Michael Williamson Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index a92f507eaff7..7bd0a55f5bbd 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -54,16 +54,12 @@ #define SPIFMT_WDELAY_SHIFT 24 #define SPIFMT_CHARLEN_MASK 0x0000001Fu -/* SPIGCR1 */ -#define SPIGCR1_SPIENA_MASK 0x01000000u /* SPIPC0 */ #define SPIPC0_DIFUN_MASK BIT(11) /* MISO */ #define SPIPC0_DOFUN_MASK BIT(10) /* MOSI */ #define SPIPC0_CLKFUN_MASK BIT(9) /* CLK */ #define SPIPC0_SPIENA_MASK BIT(8) /* nREADY */ -#define SPIPC0_EN1FUN_MASK BIT(1) -#define SPIPC0_EN0FUN_MASK BIT(0) #define SPIINT_MASKALL 0x0101035F #define SPI_INTLVL_1 0x000001FFu @@ -75,6 +71,7 @@ #define SPIGCR1_CLKMOD_MASK BIT(1) #define SPIGCR1_MASTER_MASK BIT(0) #define SPIGCR1_LOOPBACK_MASK BIT(16) +#define SPIGCR1_SPIENA_MASK BIT(24) /* SPIBUF */ #define SPIBUF_TXFULL_MASK BIT(29) @@ -90,23 +87,12 @@ #define SPIFLG_RX_INTR_MASK BIT(8) #define SPIFLG_TX_INTR_MASK BIT(9) #define SPIFLG_BUF_INIT_ACTIVE_MASK BIT(24) -#define SPIFLG_MASK (SPIFLG_DLEN_ERR_MASK \ - | SPIFLG_TIMEOUT_MASK | SPIFLG_PARERR_MASK \ - | SPIFLG_DESYNC_MASK | SPIFLG_BITERR_MASK \ - | SPIFLG_OVRRUN_MASK | SPIFLG_RX_INTR_MASK \ - | SPIFLG_TX_INTR_MASK \ - | SPIFLG_BUF_INIT_ACTIVE_MASK) - -#define SPIINT_DLEN_ERR_INTR BIT(0) -#define SPIINT_TIMEOUT_INTR BIT(1) -#define SPIINT_PARERR_INTR BIT(2) -#define SPIINT_DESYNC_INTR BIT(3) + #define SPIINT_BITERR_INTR BIT(4) #define SPIINT_OVRRUN_INTR BIT(6) #define SPIINT_RX_INTR BIT(8) #define SPIINT_TX_INTR BIT(9) #define SPIINT_DMA_REQ_EN BIT(16) -#define SPIINT_ENABLE_HIGHZ BIT(24) #define SPI_T2CDELAY_SHIFT 16 #define SPI_C2TDELAY_SHIFT 24 @@ -118,26 +104,11 @@ #define SPILVL 0x0c #define SPIFLG 0x10 #define SPIPC0 0x14 -#define SPIPC1 0x18 -#define SPIPC2 0x1c -#define SPIPC3 0x20 -#define SPIPC4 0x24 -#define SPIPC5 0x28 -#define SPIPC6 0x2c -#define SPIPC7 0x30 -#define SPIPC8 0x34 -#define SPIDAT0 0x38 #define SPIDAT1 0x3c #define SPIBUF 0x40 -#define SPIEMU 0x44 #define SPIDELAY 0x48 #define SPIDEF 0x4c #define SPIFMT0 0x50 -#define SPIFMT1 0x54 -#define SPIFMT2 0x58 -#define SPIFMT3 0x5c -#define TGINTVEC0 0x60 -#define TGINTVEC1 0x64 struct davinci_spi_slave { u32 cmd_to_write; -- cgit v1.2.2 From 50356dd7c1f6338588af6a745649a718f16fe453 Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Fri, 8 Oct 2010 15:27:26 +0530 Subject: spi: davinci: remove unnecessary typecast The typecasting of SPI base address to davinci_spi_reg is unused. Remove it. Tested-By: Michael Williamson Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 7bd0a55f5bbd..76decda0c8da 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -1020,8 +1020,7 @@ static int davinci_spi_probe(struct platform_device *pdev) goto free_master; } - davinci_spi->base = (struct davinci_spi_reg __iomem *) - ioremap(r->start, davinci_spi->region_size); + davinci_spi->base = ioremap(r->start, davinci_spi->region_size); if (davinci_spi->base == NULL) { ret = -ENOMEM; goto release_region; -- cgit v1.2.2 From 843a713bc53d04f8fac46ddd8693a2cc0422ca5e Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Thu, 12 Aug 2010 12:49:05 +0530 Subject: spi: davinci: set chip-select mode in SPIDEF only once Quit writing the same constant value determining the chip-select mode when no transmissions are in progress in davinci_spi_chipelect(). Instead just setup the SPIDEF register once during probe. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 76decda0c8da..d6b6a4958088 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -245,8 +245,6 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) * line for the controller */ if (value == BITBANG_CS_INACTIVE) { - set_io_bits(davinci_spi->base + SPIDEF, CS_DEFAULT); - data1_reg_val |= CS_DEFAULT << SPIDAT1_CSNR_SHIFT; iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); @@ -1132,6 +1130,8 @@ static int davinci_spi_probe(struct platform_device *pdev) clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); + iowrite32(CS_DEFAULT, davinci_spi->base + SPIDEF); + /* master mode default */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_MASTER_MASK); -- cgit v1.2.2 From 7978b8c385a86f0b5b9304e81a1dfb5dcaf21528 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 13 Aug 2010 10:11:03 +0530 Subject: spi: davinci: enable both activation and deactivation of chip-selects Let davinci_spi_chipselect() perform both activation and deactivation of chip selects. This lets spi_bitbang fully control chip select activation, as intended by the SPI API. With this change, the chip select activation code need not be duplicated in davinci_spi_bufs_{pio|dma}(). Also, keeping chip select active control is removed as a platform data and simply controlled using information from spi_bitbang on whether chip slect should be activated or de-activated. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 53 ++++++++++++++++------------------------------- 1 file changed, 18 insertions(+), 35 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index d6b6a4958088..105c686b2cea 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -66,7 +66,7 @@ #define SPI_INTLVL_0 0x00000000u /* SPIDAT1 */ -#define SPIDAT1_CSHOLD_SHIFT 28 +#define SPIDAT1_CSHOLD_MASK BIT(28) #define SPIDAT1_CSNR_SHIFT 16 #define SPIGCR1_CLKMOD_MASK BIT(1) #define SPIGCR1_MASTER_MASK BIT(0) @@ -235,7 +235,8 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) { struct davinci_spi *davinci_spi; struct davinci_spi_platform_data *pdata; - u32 data1_reg_val = 0; + u32 data1_reg_val; + u8 chip_sel = spi->chip_select; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; @@ -244,14 +245,17 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) * Board specific chip select logic decides the polarity and cs * line for the controller */ - if (value == BITBANG_CS_INACTIVE) { - data1_reg_val |= CS_DEFAULT << SPIDAT1_CSNR_SHIFT; - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); - - while ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_RXEMPTY_MASK) == 0) - cpu_relax(); + data1_reg_val = CS_DEFAULT << SPIDAT1_CSNR_SHIFT; + if (value == BITBANG_CS_ACTIVE) { + data1_reg_val |= SPIDAT1_CSHOLD_MASK; + data1_reg_val &= ~((0x1 << chip_sel) << SPIDAT1_CSNR_SHIFT); } + + iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + while ((ioread32(davinci_spi->base + SPIBUF) + & SPIBUF_RXEMPTY_MASK) == 0) + cpu_relax(); + } /** @@ -632,7 +636,7 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; int int_status, count, ret; - u8 conv, tmp; + u8 conv; u32 tx_data, data1_reg_val; u32 buf_val, flg_val; struct davinci_spi_platform_data *pdata; @@ -647,6 +651,8 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) conv = davinci_spi->slave[spi->chip_select].bytes_per_word; davinci_spi->count = t->len / conv; + data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + INIT_COMPLETION(davinci_spi->done); ret = davinci_spi_bufs_prep(spi, davinci_spi); @@ -661,16 +667,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) davinci_spi->base + SPIDELAY); count = davinci_spi->count; - data1_reg_val = pdata->cs_hold << SPIDAT1_CSHOLD_SHIFT; - tmp = ~(0x1 << spi->chip_select); - - clear_io_bits(davinci_spi->base + SPIDEF, ~tmp); - - data1_reg_val |= tmp << SPIDAT1_CSNR_SHIFT; - - while ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_RXEMPTY_MASK) == 0) - cpu_relax(); /* Determine the command to execute READ or WRITE */ if (t->tx_buf) { @@ -770,7 +766,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) int int_status = 0; int count, temp_count; u8 conv = 1; - u8 tmp; u32 data1_reg_val; struct davinci_spi_dma *davinci_spi_dma; int word_len, data_type, ret; @@ -794,6 +789,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) conv = davinci_spi->slave[spi->chip_select].bytes_per_word; davinci_spi->count = t->len / conv; + data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + INIT_COMPLETION(davinci_spi->done); init_completion(&davinci_spi_dma->dma_rx_completion); @@ -820,28 +817,14 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi->base + SPIDELAY); count = davinci_spi->count; /* the number of elements */ - data1_reg_val = pdata->cs_hold << SPIDAT1_CSHOLD_SHIFT; - - /* CS default = 0xFF */ - tmp = ~(0x1 << spi->chip_select); - - clear_io_bits(davinci_spi->base + SPIDEF, ~tmp); - - data1_reg_val |= tmp << SPIDAT1_CSNR_SHIFT; /* disable all interrupts for dma transfers */ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); /* Disable SPI to write configuration bits in SPIDAT */ clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - while ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_RXEMPTY_MASK) == 0) - cpu_relax(); - - if (t->tx_buf) { t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, count, DMA_TO_DEVICE); -- cgit v1.2.2 From cfbc5d1d8fda9d337e912a03502cf77d29870a8e Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Thu, 12 Aug 2010 12:27:33 +0530 Subject: spi: davinci: remove unnecessary data transmit on CS disable On TI DaVinci's SPI controller, the SPIDAT1 register which controls the chip slect status, also has data transmit register in the lower 16 bits. Writing to the whole 32-bits triggers an additional data transmit every time the chip select is disabled. While most SPI slaves cope-up with this, some cannot. This patch fixes this by doing a 16-bit write on the upper half of the SPIDAT1 register While at it, group the SPIGCR1 register related defines seperately from SPIDAT1 register defines. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 105c686b2cea..82dddf83daf7 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -65,9 +65,10 @@ #define SPI_INTLVL_1 0x000001FFu #define SPI_INTLVL_0 0x00000000u -/* SPIDAT1 */ -#define SPIDAT1_CSHOLD_MASK BIT(28) -#define SPIDAT1_CSNR_SHIFT 16 +/* SPIDAT1 (upper 16 bit defines) */ +#define SPIDAT1_CSHOLD_MASK BIT(12) + +/* SPIGCR1 */ #define SPIGCR1_CLKMOD_MASK BIT(1) #define SPIGCR1_MASTER_MASK BIT(0) #define SPIGCR1_LOOPBACK_MASK BIT(16) @@ -235,8 +236,8 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) { struct davinci_spi *davinci_spi; struct davinci_spi_platform_data *pdata; - u32 data1_reg_val; u8 chip_sel = spi->chip_select; + u16 spidat1_cfg = CS_DEFAULT; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; @@ -245,17 +246,12 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) * Board specific chip select logic decides the polarity and cs * line for the controller */ - data1_reg_val = CS_DEFAULT << SPIDAT1_CSNR_SHIFT; if (value == BITBANG_CS_ACTIVE) { - data1_reg_val |= SPIDAT1_CSHOLD_MASK; - data1_reg_val &= ~((0x1 << chip_sel) << SPIDAT1_CSNR_SHIFT); + spidat1_cfg |= SPIDAT1_CSHOLD_MASK; + spidat1_cfg &= ~(0x1 << chip_sel); } - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); - while ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_RXEMPTY_MASK) == 0) - cpu_relax(); - + iowrite16(spidat1_cfg, davinci_spi->base + SPIDAT1 + 2); } /** -- cgit v1.2.2 From 23853973d9b76eb8b3cf46157689bc6187e141d9 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 13 Aug 2010 10:57:44 +0530 Subject: spi: davinci: enable GPIO lines to be used as chip selects Sometimes, the chip selects provided by SPI module are muxed with other functionality and cannot be used in some designs. In such cases, it becomes convenient to use an available GPIO line as chip select. This patch enables the DaVinci SPI driver to treat specific GPIO lines as chip selects based on information provided in platform data. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 82dddf83daf7..d5d7014e6ae2 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -238,20 +238,32 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) struct davinci_spi_platform_data *pdata; u8 chip_sel = spi->chip_select; u16 spidat1_cfg = CS_DEFAULT; + bool gpio_chipsel = false; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; + if (pdata->chip_sel && chip_sel < pdata->num_chipselect && + pdata->chip_sel[chip_sel] != SPI_INTERN_CS) + gpio_chipsel = true; + /* * Board specific chip select logic decides the polarity and cs * line for the controller */ - if (value == BITBANG_CS_ACTIVE) { - spidat1_cfg |= SPIDAT1_CSHOLD_MASK; - spidat1_cfg &= ~(0x1 << chip_sel); - } + if (gpio_chipsel) { + if (value == BITBANG_CS_ACTIVE) + gpio_set_value(pdata->chip_sel[chip_sel], 0); + else + gpio_set_value(pdata->chip_sel[chip_sel], 1); + } else { + if (value == BITBANG_CS_ACTIVE) { + spidat1_cfg |= SPIDAT1_CSHOLD_MASK; + spidat1_cfg &= ~(0x1 << chip_sel); + } - iowrite16(spidat1_cfg, davinci_spi->base + SPIDAT1 + 2); + iowrite16(spidat1_cfg, davinci_spi->base + SPIDAT1 + 2); + } } /** @@ -546,6 +558,7 @@ static void davinci_spi_cleanup(struct spi_device *spi) static int davinci_spi_bufs_prep(struct spi_device *spi, struct davinci_spi *davinci_spi) { + struct davinci_spi_platform_data *pdata; int op_mode = 0; /* @@ -558,8 +571,12 @@ static int davinci_spi_bufs_prep(struct spi_device *spi, op_mode = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK; - if (!(spi->mode & SPI_NO_CS)) - op_mode |= 1 << spi->chip_select; + if (!(spi->mode & SPI_NO_CS)) { + pdata = davinci_spi->pdata; + if (!pdata->chip_sel || + pdata->chip_sel[spi->chip_select] == SPI_INTERN_CS) + op_mode |= 1 << spi->chip_select; + } if (spi->mode & SPI_READY) op_mode |= SPIPC0_SPIENA_MASK; @@ -1101,6 +1118,14 @@ static int davinci_spi_probe(struct platform_device *pdev) udelay(100); iowrite32(1, davinci_spi->base + SPIGCR0); + /* initialize chip selects */ + if (pdata->chip_sel) { + for (i = 0; i < pdata->num_chipselect; i++) { + if (pdata->chip_sel[i] != SPI_INTERN_CS) + gpio_direction_output(pdata->chip_sel[i], 1); + } + } + /* Clock internal */ if (davinci_spi->pdata->clk_internal) set_io_bits(davinci_spi->base + SPIGCR1, -- cgit v1.2.2 From 7fe0092b1f55f58a749d68ace3a3597e8a2a9163 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 13 Aug 2010 13:27:23 +0530 Subject: spi: davinci: simplify prescalar calculation Simplify pre-scalar calculation and move it into a seprate function. Refuse to correct invalid pre-scalar values silently as this might lead to unexpected bugs and lower performance. Instead an error will force users to dig into the root-cause of the issue. While at it, remove some device specific checks on the maximum SPI frequency. As the driver supports the SPI interface implemented on various devices, it should only take care of core SPI limitations and leave the device specific handling to platform code. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 53 +++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 22 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index d5d7014e6ae2..17269ad54a99 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -53,6 +53,7 @@ #define SPIFMT_WDELAY_MASK 0x3f000000u #define SPIFMT_WDELAY_SHIFT 24 #define SPIFMT_CHARLEN_MASK 0x0000001Fu +#define SPIFMT_PRESCALE_SHIFT 8 /* SPIPC0 */ @@ -266,6 +267,29 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) } } +/** + * davinci_spi_get_prescale - Calculates the correct prescale value + * @maxspeed_hz: the maximum rate the SPI clock can run at + * + * This function calculates the prescale value that generates a clock rate + * less than or equal to the specified maximum. + * + * Returns: calculated prescale - 1 for easy programming into SPI registers + * or negative error number if valid prescalar cannot be updated. + */ +static inline int davinci_spi_get_prescale(struct davinci_spi *davinci_spi, + u32 max_speed_hz) +{ + int ret; + + ret = DIV_ROUND_UP(clk_get_rate(davinci_spi->clk), max_speed_hz); + + if (ret < 3 || ret > 256) + return -EINVAL; + + return ret - 1; +} + /** * davinci_spi_setup_transfer - This functions will determine transfer method * @spi: spi device on which data transfer to be done @@ -281,7 +305,7 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, struct davinci_spi *davinci_spi; u8 bits_per_word = 0; - u32 hz = 0, prescale = 0, clkspeed; + u32 hz = 0, prescale = 0; davinci_spi = spi_master_get_devdata(spi->master); @@ -312,21 +336,18 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (!hz) hz = spi->max_speed_hz; + prescale = davinci_spi_get_prescale(davinci_spi, hz); + if (prescale < 0) + return prescale; + clear_fmt_bits(davinci_spi->base, SPIFMT_CHARLEN_MASK, spi->chip_select); set_fmt_bits(davinci_spi->base, bits_per_word & 0x1f, spi->chip_select); - clkspeed = clk_get_rate(davinci_spi->clk); - if (hz > clkspeed / 2) - prescale = 1 << 8; - if (hz < clkspeed / 256) - prescale = 255 << 8; - if (!prescale) - prescale = ((clkspeed / hz - 1) << 8) & 0x0000ff00; - clear_fmt_bits(davinci_spi->base, 0x0000ff00, spi->chip_select); - set_fmt_bits(davinci_spi->base, prescale, spi->chip_select); + set_fmt_bits(davinci_spi->base, + prescale << SPIFMT_PRESCALE_SHIFT, spi->chip_select); return 0; } @@ -413,10 +434,8 @@ static int davinci_spi_setup(struct spi_device *spi) int retval; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; - struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); - sdev = davinci_spi->bitbang.master->dev.parent; /* if bits per word length is zero then set it default 8 */ if (!spi->bits_per_word) @@ -435,16 +454,6 @@ static int davinci_spi_setup(struct spi_device *spi) } } - /* - * SPI in DaVinci and DA8xx operate between - * 600 KHz and 50 MHz - */ - if (spi->max_speed_hz < 600000 || spi->max_speed_hz > 50000000) { - dev_dbg(sdev, "Operating frequency is not in acceptable " - "range\n"); - return -EINVAL; - } - /* * Set up SPIFMTn register, unique to this chipselect. * -- cgit v1.2.2 From 472880c73da124b6cb5cbc31a36754aa62935afe Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Mon, 16 Aug 2010 10:28:53 +0530 Subject: spi: davinci: remove 'wait_enable' platform data member The SPI_READY bit of struct spi_device:mode serves the purpose of letting the SPI master know if the slave can signal if it is ready for transfer or not. The 'wait_enable' platform data was duplicating this functionality. Use the framework provided method of indicating this capability. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 17269ad54a99..54808577c121 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -522,7 +522,7 @@ static int davinci_spi_setup(struct spi_device *spi) SPIFMT_PARITYENA_MASK, spi->chip_select); - if (davinci_spi->pdata->wait_enable) + if (spi->mode & SPI_READY) set_fmt_bits(davinci_spi->base, SPIFMT_WAITENA_MASK, spi->chip_select); -- cgit v1.2.2 From 53a31b07c5aea4001bbb36ddd5ef2addffc7ccbd Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Mon, 16 Aug 2010 15:05:51 +0530 Subject: spi: davinci: make chip-slect specific parameters really chip-select specific Some chip-select specific paramterers like wdelay, parity, usage of chip-select timers (and the actual timer values) are included in platform data forcing the same behaviour across all chip-selects. Create a new davinci_spi_config data structure which can be passed along using controller_data member of spi_device data structure on a per-device basis. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 67 ++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 30 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 54808577c121..d4320f784070 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -156,6 +156,8 @@ struct davinci_spi { struct davinci_spi_slave slave[SPI_MAX_CHIPSELECT]; }; +static struct davinci_spi_config davinci_spi_default_cfg; + static unsigned use_dma; static void davinci_spi_rx_buf_u8(u32 data, struct davinci_spi *davinci_spi) @@ -434,8 +436,12 @@ static int davinci_spi_setup(struct spi_device *spi) int retval; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; + struct davinci_spi_config *spicfg; davinci_spi = spi_master_get_devdata(spi->master); + spicfg = (struct davinci_spi_config *)spi->controller_data; + if (!spicfg) + spicfg = &davinci_spi_default_cfg; /* if bits per word length is zero then set it default 8 */ if (!spi->bits_per_word) @@ -496,31 +502,34 @@ static int davinci_spi_setup(struct spi_device *spi) */ if (davinci_spi->version == SPI_VERSION_2) { + clear_fmt_bits(davinci_spi->base, SPIFMT_WDELAY_MASK, - spi->chip_select); + spi->chip_select); set_fmt_bits(davinci_spi->base, - (davinci_spi->pdata->wdelay - << SPIFMT_WDELAY_SHIFT) - & SPIFMT_WDELAY_MASK, - spi->chip_select); + (spicfg->wdelay << SPIFMT_WDELAY_SHIFT) & + SPIFMT_WDELAY_MASK, spi->chip_select); - if (davinci_spi->pdata->odd_parity) - set_fmt_bits(davinci_spi->base, - SPIFMT_ODD_PARITY_MASK, - spi->chip_select); + if (spicfg->odd_parity) + set_fmt_bits(davinci_spi->base, SPIFMT_ODD_PARITY_MASK, + spi->chip_select); else clear_fmt_bits(davinci_spi->base, SPIFMT_ODD_PARITY_MASK, spi->chip_select); - if (davinci_spi->pdata->parity_enable) - set_fmt_bits(davinci_spi->base, - SPIFMT_PARITYENA_MASK, - spi->chip_select); + if (spicfg->parity_enable) + set_fmt_bits(davinci_spi->base, SPIFMT_PARITYENA_MASK, + spi->chip_select); else - clear_fmt_bits(davinci_spi->base, - SPIFMT_PARITYENA_MASK, - spi->chip_select); + clear_fmt_bits(davinci_spi->base, SPIFMT_PARITYENA_MASK, + spi->chip_select); + + if (spicfg->timer_disable) + set_fmt_bits(davinci_spi->base, SPIFMT_DISTIMER_MASK, + spi->chip_select); + else + clear_fmt_bits(davinci_spi->base, SPIFMT_DISTIMER_MASK, + spi->chip_select); if (spi->mode & SPI_READY) set_fmt_bits(davinci_spi->base, @@ -531,14 +540,6 @@ static int davinci_spi_setup(struct spi_device *spi) SPIFMT_WAITENA_MASK, spi->chip_select); - if (davinci_spi->pdata->timer_disable) - set_fmt_bits(davinci_spi->base, - SPIFMT_DISTIMER_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, - SPIFMT_DISTIMER_MASK, - spi->chip_select); } retval = davinci_spi_setup_transfer(spi, NULL); @@ -662,9 +663,13 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) u32 tx_data, data1_reg_val; u32 buf_val, flg_val; struct davinci_spi_platform_data *pdata; + struct davinci_spi_config *spicfg; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; + spicfg = (struct davinci_spi_config *)spi->controller_data; + if (!spicfg) + spicfg = &davinci_spi_default_cfg; davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; @@ -684,8 +689,8 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - iowrite32(0 | (pdata->c2tdelay << SPI_C2TDELAY_SHIFT) | - (pdata->t2cdelay << SPI_T2CDELAY_SHIFT), + iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | + (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), davinci_spi->base + SPIDELAY); count = davinci_spi->count; @@ -792,12 +797,14 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) struct davinci_spi_dma *davinci_spi_dma; int word_len, data_type, ret; unsigned long tx_reg, rx_reg; - struct davinci_spi_platform_data *pdata; + struct davinci_spi_config *spicfg; struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; sdev = davinci_spi->bitbang.master->dev.parent; + spicfg = (struct davinci_spi_config *)spi->controller_data; + if (!spicfg) + spicfg = &davinci_spi_default_cfg; davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; @@ -834,8 +841,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) return ret; /* Put delay val if required */ - iowrite32(0 | (pdata->c2tdelay << SPI_C2TDELAY_SHIFT) | - (pdata->t2cdelay << SPI_T2CDELAY_SHIFT), + iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | + (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), davinci_spi->base + SPIDELAY); count = davinci_spi->count; /* the number of elements */ -- cgit v1.2.2 From 25f33512f6ae7e37d7b3d353d57d4d6d066033ce Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Thu, 19 Aug 2010 12:15:22 +0530 Subject: spi: davinci: consolidate setup of SPIFMTn in one function Consolidate the setup of SPIFMTn register under davinci_spi_setup_transfer() simplifying the code and avoiding unnecessary reads and writes to the register. The two inline functions {set|clear}_fmt_bits() can be eliminated because of this. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 154 +++++++++++++++------------------------------- 1 file changed, 49 insertions(+), 105 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index d4320f784070..34b28fe2d327 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -52,7 +52,6 @@ #define SPIFMT_ODD_PARITY_MASK BIT(23) #define SPIFMT_WDELAY_MASK 0x3f000000u #define SPIFMT_WDELAY_SHIFT 24 -#define SPIFMT_CHARLEN_MASK 0x0000001Fu #define SPIFMT_PRESCALE_SHIFT 8 @@ -212,16 +211,6 @@ static inline void clear_io_bits(void __iomem *addr, u32 bits) iowrite32(v, addr); } -static inline void set_fmt_bits(void __iomem *addr, u32 bits, int cs_num) -{ - set_io_bits(addr + SPIFMT0 + (0x4 * cs_num), bits); -} - -static inline void clear_fmt_bits(void __iomem *addr, u32 bits, int cs_num) -{ - clear_io_bits(addr + SPIFMT0 + (0x4 * cs_num), bits); -} - static void davinci_spi_set_dma_req(const struct spi_device *spi, int enable) { struct davinci_spi *davinci_spi = spi_master_get_devdata(spi->master); @@ -306,10 +295,14 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, { struct davinci_spi *davinci_spi; + struct davinci_spi_config *spicfg; u8 bits_per_word = 0; - u32 hz = 0, prescale = 0; + u32 hz = 0, spifmt = 0, prescale = 0; davinci_spi = spi_master_get_devdata(spi->master); + spicfg = (struct davinci_spi_config *)spi->controller_data; + if (!spicfg) + spicfg = &davinci_spi_default_cfg; if (t) { bits_per_word = t->bits_per_word; @@ -338,18 +331,55 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (!hz) hz = spi->max_speed_hz; + /* Set up SPIFMTn register, unique to this chipselect. */ + prescale = davinci_spi_get_prescale(davinci_spi, hz); if (prescale < 0) return prescale; - clear_fmt_bits(davinci_spi->base, SPIFMT_CHARLEN_MASK, - spi->chip_select); - set_fmt_bits(davinci_spi->base, bits_per_word & 0x1f, - spi->chip_select); + spifmt = (prescale << SPIFMT_PRESCALE_SHIFT) | (bits_per_word & 0x1f); + + if (spi->mode & SPI_LSB_FIRST) + spifmt |= SPIFMT_SHIFTDIR_MASK; + + if (spi->mode & SPI_CPOL) + spifmt |= SPIFMT_POLARITY_MASK; + + if (!(spi->mode & SPI_CPHA)) + spifmt |= SPIFMT_PHASE_MASK; + + /* + * Version 1 hardware supports two basic SPI modes: + * - Standard SPI mode uses 4 pins, with chipselect + * - 3 pin SPI is a 4 pin variant without CS (SPI_NO_CS) + * (distinct from SPI_3WIRE, with just one data wire; + * or similar variants without MOSI or without MISO) + * + * Version 2 hardware supports an optional handshaking signal, + * so it can support two more modes: + * - 5 pin SPI variant is standard SPI plus SPI_READY + * - 4 pin with enable is (SPI_READY | SPI_NO_CS) + */ + + if (davinci_spi->version == SPI_VERSION_2) { + + spifmt |= ((spicfg->wdelay << SPIFMT_WDELAY_SHIFT) + & SPIFMT_WDELAY_MASK); - clear_fmt_bits(davinci_spi->base, 0x0000ff00, spi->chip_select); - set_fmt_bits(davinci_spi->base, - prescale << SPIFMT_PRESCALE_SHIFT, spi->chip_select); + if (spicfg->odd_parity) + spifmt |= SPIFMT_ODD_PARITY_MASK; + + if (spicfg->parity_enable) + spifmt |= SPIFMT_PARITYENA_MASK; + + if (spicfg->timer_disable) + spifmt |= SPIFMT_DISTIMER_MASK; + + if (spi->mode & SPI_READY) + spifmt |= SPIFMT_WAITENA_MASK; + } + + iowrite32(spifmt, davinci_spi->base + SPIFMT0); return 0; } @@ -436,12 +466,8 @@ static int davinci_spi_setup(struct spi_device *spi) int retval; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; - struct davinci_spi_config *spicfg; davinci_spi = spi_master_get_devdata(spi->master); - spicfg = (struct davinci_spi_config *)spi->controller_data; - if (!spicfg) - spicfg = &davinci_spi_default_cfg; /* if bits per word length is zero then set it default 8 */ if (!spi->bits_per_word) @@ -460,88 +486,6 @@ static int davinci_spi_setup(struct spi_device *spi) } } - /* - * Set up SPIFMTn register, unique to this chipselect. - * - * NOTE: we could do all of these with one write. Also, some - * of the "version 2" features are found in chips that don't - * support all of them... - */ - if (spi->mode & SPI_LSB_FIRST) - set_fmt_bits(davinci_spi->base, SPIFMT_SHIFTDIR_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, SPIFMT_SHIFTDIR_MASK, - spi->chip_select); - - if (spi->mode & SPI_CPOL) - set_fmt_bits(davinci_spi->base, SPIFMT_POLARITY_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, SPIFMT_POLARITY_MASK, - spi->chip_select); - - if (!(spi->mode & SPI_CPHA)) - set_fmt_bits(davinci_spi->base, SPIFMT_PHASE_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, SPIFMT_PHASE_MASK, - spi->chip_select); - - /* - * Version 1 hardware supports two basic SPI modes: - * - Standard SPI mode uses 4 pins, with chipselect - * - 3 pin SPI is a 4 pin variant without CS (SPI_NO_CS) - * (distinct from SPI_3WIRE, with just one data wire; - * or similar variants without MOSI or without MISO) - * - * Version 2 hardware supports an optional handshaking signal, - * so it can support two more modes: - * - 5 pin SPI variant is standard SPI plus SPI_READY - * - 4 pin with enable is (SPI_READY | SPI_NO_CS) - */ - - if (davinci_spi->version == SPI_VERSION_2) { - - clear_fmt_bits(davinci_spi->base, SPIFMT_WDELAY_MASK, - spi->chip_select); - set_fmt_bits(davinci_spi->base, - (spicfg->wdelay << SPIFMT_WDELAY_SHIFT) & - SPIFMT_WDELAY_MASK, spi->chip_select); - - if (spicfg->odd_parity) - set_fmt_bits(davinci_spi->base, SPIFMT_ODD_PARITY_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, - SPIFMT_ODD_PARITY_MASK, - spi->chip_select); - - if (spicfg->parity_enable) - set_fmt_bits(davinci_spi->base, SPIFMT_PARITYENA_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, SPIFMT_PARITYENA_MASK, - spi->chip_select); - - if (spicfg->timer_disable) - set_fmt_bits(davinci_spi->base, SPIFMT_DISTIMER_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, SPIFMT_DISTIMER_MASK, - spi->chip_select); - - if (spi->mode & SPI_READY) - set_fmt_bits(davinci_spi->base, - SPIFMT_WAITENA_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, - SPIFMT_WAITENA_MASK, - spi->chip_select); - - } - retval = davinci_spi_setup_transfer(spi, NULL); return retval; -- cgit v1.2.2 From fd764463fe28ac53371565f851240e74775fb1aa Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Thu, 19 Aug 2010 12:44:31 +0530 Subject: spi: davinci: setup chip-select timers values only if timer enabled Setup chip-select timers values only if timer is enabled (timer_disbled in spi configuration is false). As a nice side effect, this patch removes code duplicated in davinci_spi_bufs_pio() and davinci_spi_bufs_dma(). Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 34b28fe2d327..d09b63cf58bf 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -374,6 +374,10 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (spicfg->timer_disable) spifmt |= SPIFMT_DISTIMER_MASK; + else + iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | + (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), + davinci_spi->base + SPIDELAY); if (spi->mode & SPI_READY) spifmt |= SPIFMT_WAITENA_MASK; @@ -607,13 +611,9 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) u32 tx_data, data1_reg_val; u32 buf_val, flg_val; struct davinci_spi_platform_data *pdata; - struct davinci_spi_config *spicfg; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; - spicfg = (struct davinci_spi_config *)spi->controller_data; - if (!spicfg) - spicfg = &davinci_spi_default_cfg; davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; @@ -633,10 +633,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | - (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), - davinci_spi->base + SPIDELAY); - count = davinci_spi->count; /* Determine the command to execute READ or WRITE */ @@ -741,14 +737,10 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) struct davinci_spi_dma *davinci_spi_dma; int word_len, data_type, ret; unsigned long tx_reg, rx_reg; - struct davinci_spi_config *spicfg; struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); sdev = davinci_spi->bitbang.master->dev.parent; - spicfg = (struct davinci_spi_config *)spi->controller_data; - if (!spicfg) - spicfg = &davinci_spi_default_cfg; davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; @@ -784,11 +776,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) if (ret) return ret; - /* Put delay val if required */ - iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | - (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), - davinci_spi->base + SPIDELAY); - count = davinci_spi->count; /* the number of elements */ /* disable all interrupts for dma transfers */ -- cgit v1.2.2 From 7abbf23c5903e14b0cff1cdeab906eab164be767 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Thu, 19 Aug 2010 15:07:38 +0530 Subject: spi: davinci: add support for wait enable timeouts Just enabling WAITENA in SPIFMTn register waits for the enable signal from the slave indefinitely. Allow support for finite waiting by adding support for c2e delay (maximum time for addressed slave to respond) and t2e delay (maximum time for slave to respond after transmit data finished). While at it, modify the T2C and C2T defines by prepending the register name as is the convention followed for other register field elsewhere in the driver. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index d09b63cf58bf..e94c63813e9f 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -78,6 +78,16 @@ #define SPIBUF_TXFULL_MASK BIT(29) #define SPIBUF_RXEMPTY_MASK BIT(31) +/* SPIDELAY */ +#define SPIDELAY_C2TDELAY_SHIFT 24 +#define SPIDELAY_C2TDELAY_MASK (0xFF << SPIDELAY_C2TDELAY_SHIFT) +#define SPIDELAY_T2CDELAY_SHIFT 16 +#define SPIDELAY_T2CDELAY_MASK (0xFF << SPIDELAY_T2CDELAY_SHIFT) +#define SPIDELAY_T2EDELAY_SHIFT 8 +#define SPIDELAY_T2EDELAY_MASK (0xFF << SPIDELAY_T2EDELAY_SHIFT) +#define SPIDELAY_C2EDELAY_SHIFT 0 +#define SPIDELAY_C2EDELAY_MASK 0xFF + /* Error Masks */ #define SPIFLG_DLEN_ERR_MASK BIT(0) #define SPIFLG_TIMEOUT_MASK BIT(1) @@ -95,9 +105,6 @@ #define SPIINT_TX_INTR BIT(9) #define SPIINT_DMA_REQ_EN BIT(16) -#define SPI_T2CDELAY_SHIFT 16 -#define SPI_C2TDELAY_SHIFT 24 - /* SPI Controller registers */ #define SPIGCR0 0x00 #define SPIGCR1 0x04 @@ -363,6 +370,8 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (davinci_spi->version == SPI_VERSION_2) { + u32 delay = 0; + spifmt |= ((spicfg->wdelay << SPIFMT_WDELAY_SHIFT) & SPIFMT_WDELAY_MASK); @@ -372,15 +381,24 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (spicfg->parity_enable) spifmt |= SPIFMT_PARITYENA_MASK; - if (spicfg->timer_disable) + if (spicfg->timer_disable) { spifmt |= SPIFMT_DISTIMER_MASK; - else - iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | - (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), - davinci_spi->base + SPIDELAY); + } else { + delay |= (spicfg->c2tdelay << SPIDELAY_C2TDELAY_SHIFT) + & SPIDELAY_C2TDELAY_MASK; + delay |= (spicfg->t2cdelay << SPIDELAY_T2CDELAY_SHIFT) + & SPIDELAY_T2CDELAY_MASK; + } - if (spi->mode & SPI_READY) + if (spi->mode & SPI_READY) { spifmt |= SPIFMT_WAITENA_MASK; + delay |= (spicfg->t2edelay << SPIDELAY_T2EDELAY_SHIFT) + & SPIDELAY_T2EDELAY_MASK; + delay |= (spicfg->c2edelay << SPIDELAY_C2EDELAY_SHIFT) + & SPIDELAY_C2EDELAY_MASK; + } + + iowrite32(delay, davinci_spi->base + SPIDELAY); } iowrite32(spifmt, davinci_spi->base + SPIFMT0); -- cgit v1.2.2 From b6c4eeac5bc6a6bf769d7f170c507a1b78fd120a Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Thu, 19 Aug 2010 15:41:41 +0530 Subject: spi: davinci: remove unused members of davinci_spi_slave Several members of struct davinci_spi_slave are unused in code. Remove such members. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index e94c63813e9f..31b9c2278dec 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -119,10 +119,7 @@ #define SPIFMT0 0x50 struct davinci_spi_slave { - u32 cmd_to_write; - u32 clk_ctrl_to_write; u32 bytes_per_word; - u8 active_cs; }; /* We have 2 DMA channels per CS, one for RX and one for TX */ @@ -495,8 +492,6 @@ static int davinci_spi_setup(struct spi_device *spi) if (!spi->bits_per_word) spi->bits_per_word = 8; - davinci_spi->slave[spi->chip_select].cmd_to_write = 0; - if (use_dma && davinci_spi->dma_channels) { davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; -- cgit v1.2.2 From cda987ebb86dfc757320bfa5c7b2afcd9d3ed30f Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Thu, 19 Aug 2010 16:16:28 +0530 Subject: spi: davinci: eliminate the single member structure davinci_spi_slave The struct davinci_spi_slave has a single member. Eliminate it and store the per-chipselect data in struct davinci_spi directly. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 31b9c2278dec..3dac2038b5a6 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -118,10 +118,6 @@ #define SPIDEF 0x4c #define SPIFMT0 0x50 -struct davinci_spi_slave { - u32 bytes_per_word; -}; - /* We have 2 DMA channels per CS, one for RX and one for TX */ struct davinci_spi_dma { int dma_tx_channel; @@ -156,7 +152,7 @@ struct davinci_spi { void (*get_rx)(u32 rx_data, struct davinci_spi *); u32 (*get_tx)(struct davinci_spi *); - struct davinci_spi_slave slave[SPI_MAX_CHIPSELECT]; + u8 bytes_per_word[SPI_MAX_CHIPSELECT]; }; static struct davinci_spi_config davinci_spi_default_cfg; @@ -324,11 +320,11 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (bits_per_word <= 8 && bits_per_word >= 2) { davinci_spi->get_rx = davinci_spi_rx_buf_u8; davinci_spi->get_tx = davinci_spi_tx_buf_u8; - davinci_spi->slave[spi->chip_select].bytes_per_word = 1; + davinci_spi->bytes_per_word[spi->chip_select] = 1; } else if (bits_per_word <= 16 && bits_per_word >= 2) { davinci_spi->get_rx = davinci_spi_rx_buf_u16; davinci_spi->get_tx = davinci_spi_tx_buf_u16; - davinci_spi->slave[spi->chip_select].bytes_per_word = 2; + davinci_spi->bytes_per_word[spi->chip_select] = 2; } else return -EINVAL; @@ -632,7 +628,7 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) davinci_spi->rx = t->rx_buf; /* convert len to words based on bits_per_word */ - conv = davinci_spi->slave[spi->chip_select].bytes_per_word; + conv = davinci_spi->bytes_per_word[spi->chip_select]; davinci_spi->count = t->len / conv; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); @@ -764,7 +760,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi->rx = t->rx_buf; /* convert len to words based on bits_per_word */ - conv = davinci_spi->slave[spi->chip_select].bytes_per_word; + conv = davinci_spi->bytes_per_word[spi->chip_select]; davinci_spi->count = t->len / conv; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); -- cgit v1.2.2 From 6321be60edac6037ea76e9beef375c6ae2961765 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Thu, 19 Aug 2010 16:38:20 +0530 Subject: spi: davinci: eliminate unnecessary update of davinci_spi->count The count member of davinci_spi is internal to the driver and is not shared with framework. Eliminate its unnecessary update. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 3dac2038b5a6..f1c3502c2d01 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -726,9 +726,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) if (ret != 0) return ret; - /* SPI Framework maintains the count only in bytes so convert back */ - davinci_spi->count *= conv; - return t->len; } @@ -880,9 +877,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) if (ret != 0) return ret; - /* SPI Framework maintains the count only in bytes so convert back */ - davinci_spi->count *= conv; - return t->len; } -- cgit v1.2.2 From b7ab24a0da96c8fc6d551ea87e82692299df6ac6 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Thu, 19 Aug 2010 16:42:42 +0530 Subject: spi: davinci: simplify calculation of edma acount value The EDMA acount (called data_type in davinci_spi_bufs_dma()) is simply the bytes_per_word obtained in the transfer setup function. The current code calculates the acount value from bytes_per_word in a convoluted manner. Simplify the code. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index f1c3502c2d01..b0b338fc4466 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -39,9 +39,6 @@ #define CS_DEFAULT 0xFF #define SPI_BUFSIZ (SMP_CACHE_BYTES + 1) -#define DAVINCI_DMA_DATA_TYPE_S8 0x01 -#define DAVINCI_DMA_DATA_TYPE_S16 0x02 -#define DAVINCI_DMA_DATA_TYPE_S32 0x04 #define SPIFMT_PHASE_MASK BIT(16) #define SPIFMT_POLARITY_MASK BIT(17) @@ -729,19 +726,14 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) return t->len; } -#define DAVINCI_DMA_DATA_TYPE_S8 0x01 -#define DAVINCI_DMA_DATA_TYPE_S16 0x02 -#define DAVINCI_DMA_DATA_TYPE_S32 0x04 - static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; int int_status = 0; int count, temp_count; - u8 conv = 1; u32 data1_reg_val; struct davinci_spi_dma *davinci_spi_dma; - int word_len, data_type, ret; + int data_type, ret; unsigned long tx_reg, rx_reg; struct device *sdev; @@ -757,8 +749,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi->rx = t->rx_buf; /* convert len to words based on bits_per_word */ - conv = davinci_spi->bytes_per_word[spi->chip_select]; - davinci_spi->count = t->len / conv; + data_type = davinci_spi->bytes_per_word[spi->chip_select]; + davinci_spi->count = t->len / data_type; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); @@ -767,17 +759,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) init_completion(&davinci_spi_dma->dma_rx_completion); init_completion(&davinci_spi_dma->dma_tx_completion); - word_len = conv * 8; - - if (word_len <= 8) - data_type = DAVINCI_DMA_DATA_TYPE_S8; - else if (word_len <= 16) - data_type = DAVINCI_DMA_DATA_TYPE_S16; - else if (word_len <= 32) - data_type = DAVINCI_DMA_DATA_TYPE_S32; - else - return -EINVAL; - ret = davinci_spi_bufs_prep(spi, davinci_spi); if (ret) return ret; -- cgit v1.2.2 From 53d454a170e86594af1d27be820e678a582af751 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Thu, 19 Aug 2010 17:04:25 +0530 Subject: spi: davinci: check for NULL buffer pointer before using it In the davinci_spi_{tx|rx}_u{8|16}() functions, check for buffer pointer being valid before using it. While providing for better error checking, this change will help simplify code in the caller. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index b0b338fc4466..10b0a08d2625 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -158,37 +158,41 @@ static unsigned use_dma; static void davinci_spi_rx_buf_u8(u32 data, struct davinci_spi *davinci_spi) { - u8 *rx = davinci_spi->rx; - - *rx++ = (u8)data; - davinci_spi->rx = rx; + if (davinci_spi->rx) { + u8 *rx = davinci_spi->rx; + *rx++ = (u8)data; + davinci_spi->rx = rx; + } } static void davinci_spi_rx_buf_u16(u32 data, struct davinci_spi *davinci_spi) { - u16 *rx = davinci_spi->rx; - - *rx++ = (u16)data; - davinci_spi->rx = rx; + if (davinci_spi->rx) { + u16 *rx = davinci_spi->rx; + *rx++ = (u16)data; + davinci_spi->rx = rx; + } } static u32 davinci_spi_tx_buf_u8(struct davinci_spi *davinci_spi) { - u32 data; - const u8 *tx = davinci_spi->tx; - - data = *tx++; - davinci_spi->tx = tx; + u32 data = 0; + if (davinci_spi->tx) { + const u8 *tx = davinci_spi->tx; + data = *tx++; + davinci_spi->tx = tx; + } return data; } static u32 davinci_spi_tx_buf_u16(struct davinci_spi *davinci_spi) { - u32 data; - const u16 *tx = davinci_spi->tx; - - data = *tx++; - davinci_spi->tx = tx; + u32 data = 0; + if (davinci_spi->tx) { + const u16 *tx = davinci_spi->tx; + data = *tx++; + davinci_spi->tx = tx; + } return data; } -- cgit v1.2.2 From fab89ea330cabdaf29d82151de6d8f2369cc4da9 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 20 Aug 2010 14:53:47 +0530 Subject: spi: davinci: remove unnecessary disable of SPI In the davinci_spi_bufs_dma() function, SPI is briefly disabled before enabling it immediately back again. Remove this unnecessary disable. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 10b0a08d2625..1169e8e7462e 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -771,8 +771,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) /* disable all interrupts for dma transfers */ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); - /* Disable SPI to write configuration bits in SPIDAT */ - clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); -- cgit v1.2.2 From f2bf4e849315c1573f996d082c53c6b686054da7 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 20 Aug 2010 15:28:23 +0530 Subject: spi: davinci: remove unnecessary 'count' variable in driver private data The variable count in DaVinci SPI driver's private data is largely unused and its minor use can easily be eliminated. Remove the variable. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 1169e8e7462e..ad814f2512f0 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -142,7 +142,6 @@ struct davinci_spi { const void *tx; void *rx; u8 *tmp_buf; - int count; struct davinci_spi_dma *dma_channels; struct davinci_spi_platform_data *pdata; @@ -630,8 +629,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* convert len to words based on bits_per_word */ conv = davinci_spi->bytes_per_word[spi->chip_select]; - davinci_spi->count = t->len / conv; - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); INIT_COMPLETION(davinci_spi->done); @@ -643,7 +640,7 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - count = davinci_spi->count; + count = t->len / conv; /* Determine the command to execute READ or WRITE */ if (t->tx_buf) { @@ -699,7 +696,7 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) } else { /* Receive in Interrupt mode */ int i; - for (i = 0; i < davinci_spi->count; i++) { + for (i = 0; i < count; i++) { set_io_bits(davinci_spi->base + SPIINT, SPIINT_BITERR_INTR | SPIINT_OVRRUN_INTR @@ -754,7 +751,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) /* convert len to words based on bits_per_word */ data_type = davinci_spi->bytes_per_word[spi->chip_select]; - davinci_spi->count = t->len / data_type; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); @@ -767,7 +763,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) if (ret) return ret; - count = davinci_spi->count; /* the number of elements */ + count = t->len / data_type; /* the number of elements */ /* disable all interrupts for dma transfers */ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); -- cgit v1.2.2 From 134e32b979f3f3987e84dcd5cc42040e21252e53 Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Fri, 20 Aug 2010 16:27:08 +0530 Subject: spi: davinci: do not treat Tx interrupt being set as error In davinci_spi_check_error(), Tx interrupt being set is treated as error. This function is only meant to flag bus error conditions and Tx interrupt being set at that point is not a bus error but rather a driver bug. Stop checking for Tx interrupt and flagging that as an IO error. Tested-By: Michael Williamson Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index ad814f2512f0..a60a65c690de 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -590,10 +590,6 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, dev_dbg(sdev, "SPI Data Overrun error\n"); return -EIO; } - if (int_status & SPIFLG_TX_INTR_MASK) { - dev_dbg(sdev, "SPI TX intr bit set\n"); - return -EIO; - } if (int_status & SPIFLG_BUF_INIT_ACTIVE_MASK) { dev_dbg(sdev, "SPI Buffer Init Active\n"); return -EBUSY; -- cgit v1.2.2 From 47f44671c0dc92e2b77ff3dd843f742d12510477 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Mon, 23 Aug 2010 16:34:13 +0530 Subject: spi: davinci: remove unnecessary completion variable initialization The completion variable 'done' is unnecessarly initialized by the function davinci_spi_bufs_dma() where as it is not used for DMA transfers at all. Remove the unnecessary initialization. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index a60a65c690de..54d06f409b65 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -750,8 +750,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); - INIT_COMPLETION(davinci_spi->done); - init_completion(&davinci_spi_dma->dma_rx_completion); init_completion(&davinci_spi_dma->dma_tx_completion); -- cgit v1.2.2 From cf90fe73504764cbcc2552c7ea69b1866059db30 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 20 Aug 2010 17:02:49 +0530 Subject: spi: davinci: remove non-useful interrupt mode support The interrupt mode support as it stands is another version of poll mode. Even when interrupt mode is selected, the code tight loops on interrupt status register, rendering it totally useless. A completion variable is initialized, but never used. Remove this fake interrupt mode since users can anyway use poll mode with no functional difference. A usefully implemented interrupt mode support can be added later. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 126 +++++++--------------------------------------- 1 file changed, 19 insertions(+), 107 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 54d06f409b65..198f062da370 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -59,8 +59,6 @@ #define SPIPC0_SPIENA_MASK BIT(8) /* nREADY */ #define SPIINT_MASKALL 0x0101035F -#define SPI_INTLVL_1 0x000001FFu -#define SPI_INTLVL_0 0x00000000u /* SPIDAT1 (upper 16 bit defines) */ #define SPIDAT1_CSHOLD_MASK BIT(12) @@ -92,14 +90,8 @@ #define SPIFLG_DESYNC_MASK BIT(3) #define SPIFLG_BITERR_MASK BIT(4) #define SPIFLG_OVRRUN_MASK BIT(6) -#define SPIFLG_RX_INTR_MASK BIT(8) -#define SPIFLG_TX_INTR_MASK BIT(9) #define SPIFLG_BUF_INIT_ACTIVE_MASK BIT(24) -#define SPIINT_BITERR_INTR BIT(4) -#define SPIINT_OVRRUN_INTR BIT(6) -#define SPIINT_RX_INTR BIT(8) -#define SPIINT_TX_INTR BIT(9) #define SPIINT_DMA_REQ_EN BIT(16) /* SPI Controller registers */ @@ -136,8 +128,6 @@ struct davinci_spi { resource_size_t pbase; void __iomem *base; size_t region_size; - u32 irq; - struct completion done; const void *tx; void *rx; @@ -611,7 +601,7 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; - int int_status, count, ret; + int status, count, ret; u8 conv; u32 tx_data, data1_reg_val; u32 buf_val, flg_val; @@ -627,8 +617,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) conv = davinci_spi->bytes_per_word[spi->chip_select]; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); - INIT_COMPLETION(davinci_spi->done); - ret = davinci_spi_bufs_prep(spi, davinci_spi); if (ret) return ret; @@ -638,9 +626,10 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) count = t->len / conv; + clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); + /* Determine the command to execute READ or WRITE */ if (t->tx_buf) { - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); while (1) { tx_data = davinci_spi->get_tx(davinci_spi); @@ -668,45 +657,25 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) break; } } else { - if (pdata->poll_mode) { - while (1) { - /* keeps the serial clock going */ - if ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_TXFULL_MASK) == 0) - iowrite32(data1_reg_val, - davinci_spi->base + SPIDAT1); + while (1) { + /* keeps the serial clock going */ + if ((ioread32(davinci_spi->base + SPIBUF) + & SPIBUF_TXFULL_MASK) == 0) + iowrite32(data1_reg_val, + davinci_spi->base + SPIDAT1); while (ioread32(davinci_spi->base + SPIBUF) & - SPIBUF_RXEMPTY_MASK) + SPIBUF_RXEMPTY_MASK) cpu_relax(); - flg_val = ioread32(davinci_spi->base + SPIFLG); - buf_val = ioread32(davinci_spi->base + SPIBUF); - - davinci_spi->get_rx(buf_val, davinci_spi); - - count--; - if (count <= 0) - break; - } - } else { /* Receive in Interrupt mode */ - int i; - - for (i = 0; i < count; i++) { - set_io_bits(davinci_spi->base + SPIINT, - SPIINT_BITERR_INTR - | SPIINT_OVRRUN_INTR - | SPIINT_RX_INTR); + flg_val = ioread32(davinci_spi->base + SPIFLG); + buf_val = ioread32(davinci_spi->base + SPIBUF); - iowrite32(data1_reg_val, - davinci_spi->base + SPIDAT1); + davinci_spi->get_rx(buf_val, davinci_spi); - while (ioread32(davinci_spi->base + SPIINT) & - SPIINT_RX_INTR) - cpu_relax(); - } - iowrite32((data1_reg_val & 0x0ffcffff), - davinci_spi->base + SPIDAT1); + count--; + if (count <= 0) + break; } } @@ -714,9 +683,9 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) * Check for bit error, desync error,parity error,timeout error and * receive overflow errors */ - int_status = ioread32(davinci_spi->base + SPIFLG); + status = ioread32(davinci_spi->base + SPIFLG); - ret = davinci_spi_check_error(davinci_spi, int_status); + ret = davinci_spi_check_error(davinci_spi, status); if (ret != 0) return ret; @@ -853,38 +822,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) return t->len; } -/** - * davinci_spi_irq - IRQ handler for DaVinci SPI - * @irq: IRQ number for this SPI Master - * @context_data: structure for SPI Master controller davinci_spi - */ -static irqreturn_t davinci_spi_irq(s32 irq, void *context_data) -{ - struct davinci_spi *davinci_spi = context_data; - u32 int_status, rx_data = 0; - irqreturn_t ret = IRQ_NONE; - - int_status = ioread32(davinci_spi->base + SPIFLG); - - while ((int_status & SPIFLG_RX_INTR_MASK)) { - if (likely(int_status & SPIFLG_RX_INTR_MASK)) { - ret = IRQ_HANDLED; - - rx_data = ioread32(davinci_spi->base + SPIBUF); - davinci_spi->get_rx(rx_data, davinci_spi); - - /* Disable Receive Interrupt */ - iowrite32(~(SPIINT_RX_INTR | SPIINT_TX_INTR), - davinci_spi->base + SPIINT); - } else - (void)davinci_spi_check_error(davinci_spi, int_status); - - int_status = ioread32(davinci_spi->base + SPIFLG); - } - - return ret; -} - /** * davinci_spi_probe - probe function for SPI Master Controller * @pdev: platform_device structure which contains plateform specific data @@ -943,22 +880,11 @@ static int davinci_spi_probe(struct platform_device *pdev) goto release_region; } - davinci_spi->irq = platform_get_irq(pdev, 0); - if (davinci_spi->irq <= 0) { - ret = -EINVAL; - goto unmap_io; - } - - ret = request_irq(davinci_spi->irq, davinci_spi_irq, IRQF_DISABLED, - dev_name(&pdev->dev), davinci_spi); - if (ret) - goto unmap_io; - /* Allocate tmp_buf for tx_buf */ davinci_spi->tmp_buf = kzalloc(SPI_BUFSIZ, GFP_KERNEL); if (davinci_spi->tmp_buf == NULL) { ret = -ENOMEM; - goto irq_free; + goto unmap_io; } davinci_spi->bitbang.master = spi_master_get(master); @@ -1034,8 +960,6 @@ static int davinci_spi_probe(struct platform_device *pdev) davinci_spi->get_rx = davinci_spi_rx_buf_u8; davinci_spi->get_tx = davinci_spi_tx_buf_u8; - init_completion(&davinci_spi->done); - /* Reset In/OUT SPI module */ iowrite32(0, davinci_spi->base + SPIGCR0); udelay(100); @@ -1062,21 +986,12 @@ static int davinci_spi_probe(struct platform_device *pdev) /* master mode default */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_MASTER_MASK); - if (davinci_spi->pdata->intr_level) - iowrite32(SPI_INTLVL_1, davinci_spi->base + SPILVL); - else - iowrite32(SPI_INTLVL_0, davinci_spi->base + SPILVL); - ret = spi_bitbang_start(&davinci_spi->bitbang); if (ret) goto free_clk; dev_info(&pdev->dev, "Controller at 0x%p\n", davinci_spi->base); - if (!pdata->poll_mode) - dev_info(&pdev->dev, "Operating in interrupt mode" - " using IRQ %d\n", davinci_spi->irq); - return ret; free_clk: @@ -1086,8 +1001,6 @@ put_master: spi_master_put(master); free_tmp_buf: kfree(davinci_spi->tmp_buf); -irq_free: - free_irq(davinci_spi->irq, davinci_spi); unmap_io: iounmap(davinci_spi->base); release_region: @@ -1121,7 +1034,6 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) clk_put(davinci_spi->clk); spi_master_put(master); kfree(davinci_spi->tmp_buf); - free_irq(davinci_spi->irq, davinci_spi); iounmap(davinci_spi->base); release_mem_region(davinci_spi->pbase, davinci_spi->region_size); -- cgit v1.2.2 From 839c996ca8dd56f9ea80d7fc0c8b18b01394c82a Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Mon, 23 Aug 2010 16:39:19 +0530 Subject: spi: davinci: simplify poll mode transfers Use the fact that the get_tx and get_rx can now cope with NULL buffer pointers to simplify the poll mode transfer code. While at it, check for SPI errors every transfer rather than at the end of the whole transfer. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 96 ++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 55 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 198f062da370..cd37697850cf 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -91,6 +91,10 @@ #define SPIFLG_BITERR_MASK BIT(4) #define SPIFLG_OVRRUN_MASK BIT(6) #define SPIFLG_BUF_INIT_ACTIVE_MASK BIT(24) +#define SPIFLG_ERROR_MASK (SPIFLG_DLEN_ERR_MASK \ + | SPIFLG_TIMEOUT_MASK | SPIFLG_PARERR_MASK \ + | SPIFLG_DESYNC_MASK | SPIFLG_BITERR_MASK \ + | SPIFLG_OVRRUN_MASK) #define SPIINT_DMA_REQ_EN BIT(16) @@ -601,10 +605,10 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; - int status, count, ret; - u8 conv; + int ret; + int rcount, wcount; u32 tx_data, data1_reg_val; - u32 buf_val, flg_val; + u32 errors = 0; struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); @@ -612,70 +616,51 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; - - /* convert len to words based on bits_per_word */ - conv = davinci_spi->bytes_per_word[spi->chip_select]; - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + wcount = t->len / davinci_spi->bytes_per_word[spi->chip_select]; + rcount = wcount; ret = davinci_spi_bufs_prep(spi, davinci_spi); if (ret) return ret; + data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - count = t->len / conv; - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); - /* Determine the command to execute READ or WRITE */ - if (t->tx_buf) { + /* start the transfer */ + wcount--; + tx_data = davinci_spi->get_tx(davinci_spi); + data1_reg_val &= 0xFFFF0000; + data1_reg_val |= tx_data & 0xFFFF; + iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); - while (1) { - tx_data = davinci_spi->get_tx(davinci_spi); + while (rcount > 0 || wcount > 0) { - data1_reg_val &= ~(0xFFFF); - data1_reg_val |= (0xFFFF & tx_data); - - buf_val = ioread32(davinci_spi->base + SPIBUF); - if ((buf_val & SPIBUF_TXFULL_MASK) == 0) { - iowrite32(data1_reg_val, - davinci_spi->base + SPIDAT1); - - count--; - } - while (ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_RXEMPTY_MASK) - cpu_relax(); - - /* getting the returned byte */ - if (t->rx_buf) { - buf_val = ioread32(davinci_spi->base + SPIBUF); - davinci_spi->get_rx(buf_val, davinci_spi); - } - if (count <= 0) - break; - } - } else { - while (1) { - /* keeps the serial clock going */ - if ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_TXFULL_MASK) == 0) - iowrite32(data1_reg_val, - davinci_spi->base + SPIDAT1); + u32 buf, status; - while (ioread32(davinci_spi->base + SPIBUF) & - SPIBUF_RXEMPTY_MASK) - cpu_relax(); + buf = ioread32(davinci_spi->base + SPIBUF); - flg_val = ioread32(davinci_spi->base + SPIFLG); - buf_val = ioread32(davinci_spi->base + SPIBUF); + if (!(buf & SPIBUF_RXEMPTY_MASK)) { + davinci_spi->get_rx(buf & 0xFFFF, davinci_spi); + rcount--; + } - davinci_spi->get_rx(buf_val, davinci_spi); + status = ioread32(davinci_spi->base + SPIFLG); - count--; - if (count <= 0) - break; + if (unlikely(status & SPIFLG_ERROR_MASK)) { + errors = status & SPIFLG_ERROR_MASK; + break; + } + + if (wcount > 0 && !(buf & SPIBUF_TXFULL_MASK)) { + wcount--; + tx_data = davinci_spi->get_tx(davinci_spi); + data1_reg_val &= ~0xFFFF; + data1_reg_val |= 0xFFFF & tx_data; + iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); } } @@ -683,11 +668,12 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) * Check for bit error, desync error,parity error,timeout error and * receive overflow errors */ - status = ioread32(davinci_spi->base + SPIFLG); - - ret = davinci_spi_check_error(davinci_spi, status); - if (ret != 0) + if (errors) { + ret = davinci_spi_check_error(davinci_spi, errors); + WARN(!ret, "%s: error reported but no error found!\n", + dev_name(&spi->dev)); return ret; + } return t->len; } -- cgit v1.2.2 From e0d205e9914476e96596c5339fa440fb314ef03b Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Thu, 2 Sep 2010 16:52:06 +0530 Subject: spi: davinci: add support for interrupt mode Add support for SPI interrupt mode operation. Define a per chip-select "io type" variable which specifies if the transfers on this chip-select should happen in interrupt mode or polled mode. Introduce a new function davinci_spi_process_events() to help consolidate the code between interrupt mode processing and polled mode processing. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 145 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 115 insertions(+), 30 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index cd37697850cf..45cc1a77a512 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -59,6 +59,9 @@ #define SPIPC0_SPIENA_MASK BIT(8) /* nREADY */ #define SPIINT_MASKALL 0x0101035F +#define SPIINT_MASKINT 0x0000015F +#define SPI_INTLVL_1 0x000001FF +#define SPI_INTLVL_0 0x00000000 /* SPIDAT1 (upper 16 bit defines) */ #define SPIDAT1_CSHOLD_MASK BIT(12) @@ -132,10 +135,14 @@ struct davinci_spi { resource_size_t pbase; void __iomem *base; size_t region_size; + u32 irq; + struct completion done; const void *tx; void *rx; u8 *tmp_buf; + int rcount; + int wcount; struct davinci_spi_dma *dma_channels; struct davinci_spi_platform_data *pdata; @@ -593,6 +600,43 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, return 0; } +/** + * davinci_spi_process_events - check for and handle any SPI controller events + * @davinci_spi: the controller data + * + * This function will check the SPIFLG register and handle any events that are + * detected there + */ +static int davinci_spi_process_events(struct davinci_spi *davinci_spi) +{ + u32 buf, status, errors = 0, data1_reg_val; + + buf = ioread32(davinci_spi->base + SPIBUF); + + if (davinci_spi->rcount > 0 && !(buf & SPIBUF_RXEMPTY_MASK)) { + davinci_spi->get_rx(buf & 0xFFFF, davinci_spi); + davinci_spi->rcount--; + } + + status = ioread32(davinci_spi->base + SPIFLG); + + if (unlikely(status & SPIFLG_ERROR_MASK)) { + errors = status & SPIFLG_ERROR_MASK; + goto out; + } + + if (davinci_spi->wcount > 0 && !(buf & SPIBUF_TXFULL_MASK)) { + data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + davinci_spi->wcount--; + data1_reg_val &= ~0xFFFF; + data1_reg_val |= 0xFFFF & davinci_spi->get_tx(davinci_spi); + iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + } + +out: + return errors; +} + /** * davinci_spi_bufs - functions which will handle transfer data * @spi: spi device on which data transfer to be done @@ -606,18 +650,22 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; int ret; - int rcount, wcount; u32 tx_data, data1_reg_val; u32 errors = 0; + struct davinci_spi_config *spicfg; struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; + spicfg = (struct davinci_spi_config *)spi->controller_data; + if (!spicfg) + spicfg = &davinci_spi_default_cfg; davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; - wcount = t->len / davinci_spi->bytes_per_word[spi->chip_select]; - rcount = wcount; + davinci_spi->wcount = t->len / + davinci_spi->bytes_per_word[spi->chip_select]; + davinci_spi->rcount = davinci_spi->wcount; ret = davinci_spi_bufs_prep(spi, davinci_spi); if (ret) @@ -628,42 +676,32 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); + if (spicfg->io_type == SPI_IO_TYPE_INTR) { + set_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKINT); + INIT_COMPLETION(davinci_spi->done); + } /* start the transfer */ - wcount--; + davinci_spi->wcount--; tx_data = davinci_spi->get_tx(davinci_spi); data1_reg_val &= 0xFFFF0000; data1_reg_val |= tx_data & 0xFFFF; iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); - while (rcount > 0 || wcount > 0) { - - u32 buf, status; - - buf = ioread32(davinci_spi->base + SPIBUF); - - if (!(buf & SPIBUF_RXEMPTY_MASK)) { - davinci_spi->get_rx(buf & 0xFFFF, davinci_spi); - rcount--; - } - - status = ioread32(davinci_spi->base + SPIFLG); - - if (unlikely(status & SPIFLG_ERROR_MASK)) { - errors = status & SPIFLG_ERROR_MASK; - break; - } - - if (wcount > 0 && !(buf & SPIBUF_TXFULL_MASK)) { - wcount--; - tx_data = davinci_spi->get_tx(davinci_spi); - data1_reg_val &= ~0xFFFF; - data1_reg_val |= 0xFFFF & tx_data; - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + /* Wait for the transfer to complete */ + if (spicfg->io_type == SPI_IO_TYPE_INTR) { + wait_for_completion_interruptible(&(davinci_spi->done)); + } else { + while (davinci_spi->rcount > 0 || davinci_spi->wcount > 0) { + errors = davinci_spi_process_events(davinci_spi); + if (errors) + break; + cpu_relax(); } } + clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); + /* * Check for bit error, desync error,parity error,timeout error and * receive overflow errors @@ -678,6 +716,32 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) return t->len; } +/** + * davinci_spi_irq - Interrupt handler for SPI Master Controller + * @irq: IRQ number for this SPI Master + * @context_data: structure for SPI Master controller davinci_spi + * + * ISR will determine that interrupt arrives either for READ or WRITE command. + * According to command it will do the appropriate action. It will check + * transfer length and if it is not zero then dispatch transfer command again. + * If transfer length is zero then it will indicate the COMPLETION so that + * davinci_spi_bufs function can go ahead. + */ +static irqreturn_t davinci_spi_irq(s32 irq, void *context_data) +{ + struct davinci_spi *davinci_spi = context_data; + int status; + + status = davinci_spi_process_events(davinci_spi); + if (unlikely(status != 0)) + clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKINT); + + if ((!davinci_spi->rcount && !davinci_spi->wcount) || status) + complete(&davinci_spi->done); + + return IRQ_HANDLED; +} + static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; @@ -866,11 +930,22 @@ static int davinci_spi_probe(struct platform_device *pdev) goto release_region; } + davinci_spi->irq = platform_get_irq(pdev, 0); + if (davinci_spi->irq <= 0) { + ret = -EINVAL; + goto unmap_io; + } + + ret = request_irq(davinci_spi->irq, davinci_spi_irq, 0, + dev_name(&pdev->dev), davinci_spi); + if (ret) + goto unmap_io; + /* Allocate tmp_buf for tx_buf */ davinci_spi->tmp_buf = kzalloc(SPI_BUFSIZ, GFP_KERNEL); if (davinci_spi->tmp_buf == NULL) { ret = -ENOMEM; - goto unmap_io; + goto irq_free; } davinci_spi->bitbang.master = spi_master_get(master); @@ -946,6 +1021,8 @@ static int davinci_spi_probe(struct platform_device *pdev) davinci_spi->get_rx = davinci_spi_rx_buf_u8; davinci_spi->get_tx = davinci_spi_tx_buf_u8; + init_completion(&davinci_spi->done); + /* Reset In/OUT SPI module */ iowrite32(0, davinci_spi->base + SPIGCR0); udelay(100); @@ -967,6 +1044,11 @@ static int davinci_spi_probe(struct platform_device *pdev) clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); + if (pdata->intr_line) + iowrite32(SPI_INTLVL_1, davinci_spi->base + SPILVL); + else + iowrite32(SPI_INTLVL_0, davinci_spi->base + SPILVL); + iowrite32(CS_DEFAULT, davinci_spi->base + SPIDEF); /* master mode default */ @@ -987,6 +1069,8 @@ put_master: spi_master_put(master); free_tmp_buf: kfree(davinci_spi->tmp_buf); +irq_free: + free_irq(davinci_spi->irq, davinci_spi); unmap_io: iounmap(davinci_spi->base); release_region: @@ -1020,6 +1104,7 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) clk_put(davinci_spi->clk); spi_master_put(master); kfree(davinci_spi->tmp_buf); + free_irq(davinci_spi->irq, davinci_spi); iounmap(davinci_spi->base); release_mem_region(davinci_spi->pbase, davinci_spi->region_size); -- cgit v1.2.2 From f34bd4cc68fb4548536cac56798d3fad41806724 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 3 Sep 2010 11:56:35 +0530 Subject: spi: davinci: configure the invariable bits in spipc0 only once Configure the data-in, data-out and clock functionality pins in SPIPC0 register only once during probe. No need to set these bits for each transfer. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 45cc1a77a512..6f279c5d8f94 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -536,9 +536,6 @@ static int davinci_spi_bufs_prep(struct spi_device *spi, * optimize for both flags staying cleared. */ - op_mode = SPIPC0_DIFUN_MASK - | SPIPC0_DOFUN_MASK - | SPIPC0_CLKFUN_MASK; if (!(spi->mode & SPI_NO_CS)) { pdata = davinci_spi->pdata; if (!pdata->chip_sel || @@ -886,6 +883,7 @@ static int davinci_spi_probe(struct platform_device *pdev) resource_size_t dma_tx_chan = SPI_NO_RESOURCE; resource_size_t dma_eventq = SPI_NO_RESOURCE; int i = 0, ret = 0; + u32 spipc0; pdata = pdev->dev.platform_data; if (pdata == NULL) { @@ -1028,6 +1026,10 @@ static int davinci_spi_probe(struct platform_device *pdev) udelay(100); iowrite32(1, davinci_spi->base + SPIGCR0); + /* Set up SPIPC0. CS and ENA init is done in davinci_spi_bufs_prep */ + spipc0 = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK; + iowrite32(spipc0, davinci_spi->base + SPIPC0); + /* initialize chip selects */ if (pdata->chip_sel) { for (i = 0; i < pdata->num_chipselect; i++) { -- cgit v1.2.2 From be88471b96cf3a0d7aea72d5ca9c6a95fb54bade Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 3 Sep 2010 12:15:28 +0530 Subject: spi: davinci: remove unnecessary function davinci_spi_bufs_prep() The function davinci_spi_bufs_prep() is doing stuff that davinci_spi_setup() is doing. Eliminate it and move the work to davinci_spi_setup() Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 64 ++++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 43 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 6f279c5d8f94..05b6145da3ef 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -482,13 +482,33 @@ static int davinci_spi_setup(struct spi_device *spi) int retval; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; + struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); + pdata = davinci_spi->pdata; /* if bits per word length is zero then set it default 8 */ if (!spi->bits_per_word) spi->bits_per_word = 8; + if (!(spi->mode & SPI_NO_CS)) { + if ((pdata->chip_sel == NULL) || + (pdata->chip_sel[spi->chip_select] == SPI_INTERN_CS)) + set_io_bits(davinci_spi->base + SPIPC0, + 1 << spi->chip_select); + + } + + if (spi->mode & SPI_READY) + set_io_bits(davinci_spi->base + SPIPC0, SPIPC0_SPIENA_MASK); + + if (spi->mode & SPI_LOOP) + set_io_bits(davinci_spi->base + SPIGCR1, + SPIGCR1_LOOPBACK_MASK); + else + clear_io_bits(davinci_spi->base + SPIGCR1, + SPIGCR1_LOOPBACK_MASK); + if (use_dma && davinci_spi->dma_channels) { davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; @@ -523,40 +543,6 @@ static void davinci_spi_cleanup(struct spi_device *spi) } } -static int davinci_spi_bufs_prep(struct spi_device *spi, - struct davinci_spi *davinci_spi) -{ - struct davinci_spi_platform_data *pdata; - int op_mode = 0; - - /* - * REVISIT unless devices disagree about SPI_LOOP or - * SPI_READY (SPI_NO_CS only allows one device!), this - * should not need to be done before each message... - * optimize for both flags staying cleared. - */ - - if (!(spi->mode & SPI_NO_CS)) { - pdata = davinci_spi->pdata; - if (!pdata->chip_sel || - pdata->chip_sel[spi->chip_select] == SPI_INTERN_CS) - op_mode |= 1 << spi->chip_select; - } - if (spi->mode & SPI_READY) - op_mode |= SPIPC0_SPIENA_MASK; - - iowrite32(op_mode, davinci_spi->base + SPIPC0); - - if (spi->mode & SPI_LOOP) - set_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_LOOPBACK_MASK); - else - clear_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_LOOPBACK_MASK); - - return 0; -} - static int davinci_spi_check_error(struct davinci_spi *davinci_spi, int int_status) { @@ -664,10 +650,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) davinci_spi->bytes_per_word[spi->chip_select]; davinci_spi->rcount = davinci_spi->wcount; - ret = davinci_spi_bufs_prep(spi, davinci_spi); - if (ret) - return ret; - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); /* Enable SPI */ @@ -769,10 +751,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) init_completion(&davinci_spi_dma->dma_rx_completion); init_completion(&davinci_spi_dma->dma_tx_completion); - ret = davinci_spi_bufs_prep(spi, davinci_spi); - if (ret) - return ret; - count = t->len / data_type; /* the number of elements */ /* disable all interrupts for dma transfers */ @@ -1026,7 +1004,7 @@ static int davinci_spi_probe(struct platform_device *pdev) udelay(100); iowrite32(1, davinci_spi->base + SPIGCR0); - /* Set up SPIPC0. CS and ENA init is done in davinci_spi_bufs_prep */ + /* Set up SPIPC0. CS and ENA init is done in davinci_spi_setup */ spipc0 = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK; iowrite32(spipc0, davinci_spi->base + SPIPC0); -- cgit v1.2.2 From b23a5d4691043e97bbfde8c2fb5b8fecdc400308 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 24 Sep 2010 18:53:32 +0530 Subject: spi: davinci: remove unnecessary call to davinci_spi_setup_transfer() Remove unnecessary call to davinci_spi_setup_transfer() at the end of davinci_spi_setup(). davinci_spi_setup_transfer() is registered as the setup_transfer callback for the bitbang layer and is called independently by the bitbang layer to setup the transfer before it begins. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 05b6145da3ef..77109dc11b09 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -479,7 +479,7 @@ static int davinci_spi_request_dma(struct spi_device *spi) */ static int davinci_spi_setup(struct spi_device *spi) { - int retval; + int retval = 0; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; struct davinci_spi_platform_data *pdata; @@ -512,16 +512,11 @@ static int davinci_spi_setup(struct spi_device *spi) if (use_dma && davinci_spi->dma_channels) { davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; - if ((davinci_spi_dma->dma_rx_channel == -1) - || (davinci_spi_dma->dma_tx_channel == -1)) { + if ((davinci_spi_dma->dma_rx_channel == -1) || + (davinci_spi_dma->dma_tx_channel == -1)) retval = davinci_spi_request_dma(spi); - if (retval < 0) - return retval; - } } - retval = davinci_spi_setup_transfer(spi, NULL); - return retval; } -- cgit v1.2.2 From 96fd881f22b44fc14772316a6b9231012393cda8 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Mon, 27 Sep 2010 22:23:23 +0530 Subject: spi: davinci: do not store DMA channel information per chip select Do not store DMA channel related information per chip-select since that information does not depend on the chip select. The same DMA channels can be used for transfers on all chip-selects since the transfer happens one-at-a-time. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 47 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 77109dc11b09..4aa522134a90 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -143,7 +143,7 @@ struct davinci_spi { u8 *tmp_buf; int rcount; int wcount; - struct davinci_spi_dma *dma_channels; + struct davinci_spi_dma dma_channels; struct davinci_spi_platform_data *pdata; void (*get_rx)(u32 rx_data, struct davinci_spi *); @@ -407,7 +407,7 @@ static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) struct davinci_spi_dma *davinci_spi_dma; davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]); + davinci_spi_dma = &davinci_spi->dma_channels; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_rx_channel); @@ -426,7 +426,7 @@ static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) struct davinci_spi_dma *davinci_spi_dma; davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]); + davinci_spi_dma = &davinci_spi->dma_channels; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_tx_channel); @@ -446,7 +446,7 @@ static int davinci_spi_request_dma(struct spi_device *spi) int r; davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; + davinci_spi_dma = &davinci_spi->dma_channels; sdev = davinci_spi->bitbang.master->dev.parent; r = edma_alloc_channel(davinci_spi_dma->dma_rx_sync_dev, @@ -509,8 +509,8 @@ static int davinci_spi_setup(struct spi_device *spi) clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_LOOPBACK_MASK); - if (use_dma && davinci_spi->dma_channels) { - davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; + if (use_dma) { + davinci_spi_dma = &davinci_spi->dma_channels; if ((davinci_spi_dma->dma_rx_channel == -1) || (davinci_spi_dma->dma_tx_channel == -1)) @@ -522,13 +522,11 @@ static int davinci_spi_setup(struct spi_device *spi) static void davinci_spi_cleanup(struct spi_device *spi) { - struct davinci_spi *davinci_spi = spi_master_get_devdata(spi->master); - struct davinci_spi_dma *davinci_spi_dma; - - davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; - - if (use_dma && davinci_spi->dma_channels) { - davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; + if (use_dma) { + struct davinci_spi *davinci_spi = + spi_master_get_devdata(spi->master); + struct davinci_spi_dma *davinci_spi_dma = + &davinci_spi->dma_channels; if ((davinci_spi_dma->dma_rx_channel != -1) && (davinci_spi_dma->dma_tx_channel != -1)) { @@ -730,7 +728,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi = spi_master_get_devdata(spi->master); sdev = davinci_spi->bitbang.master->dev.parent; - davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; + davinci_spi_dma = &davinci_spi->dma_channels; tx_reg = (unsigned long)davinci_spi->pbase + SPIDAT1; rx_reg = (unsigned long)davinci_spi->pbase + SPIBUF; @@ -967,22 +965,13 @@ static int davinci_spi_probe(struct platform_device *pdev) use_dma = 0; } else { davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_dma; - davinci_spi->dma_channels = kzalloc(master->num_chipselect - * sizeof(struct davinci_spi_dma), GFP_KERNEL); - if (davinci_spi->dma_channels == NULL) { - ret = -ENOMEM; - goto free_clk; - } - for (i = 0; i < master->num_chipselect; i++) { - davinci_spi->dma_channels[i].dma_rx_channel = -1; - davinci_spi->dma_channels[i].dma_rx_sync_dev = - dma_rx_chan; - davinci_spi->dma_channels[i].dma_tx_channel = -1; - davinci_spi->dma_channels[i].dma_tx_sync_dev = - dma_tx_chan; - davinci_spi->dma_channels[i].eventq = dma_eventq; - } + davinci_spi->dma_channels.dma_rx_channel = -1; + davinci_spi->dma_channels.dma_rx_sync_dev = dma_rx_chan; + davinci_spi->dma_channels.dma_tx_channel = -1; + davinci_spi->dma_channels.dma_tx_sync_dev = dma_tx_chan; + davinci_spi->dma_channels.eventq = dma_eventq; + dev_info(&pdev->dev, "DaVinci SPI driver in EDMA mode\n" "Using RX channel = %d , TX channel = %d and " "event queue = %d", dma_rx_chan, dma_tx_chan, -- cgit v1.2.2 From c29e3c60e75d1cc1262ac8af379738b6fd851f33 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Tue, 28 Sep 2010 13:59:26 +0530 Subject: spi: davinci: always start transmit DMA Due to the full duplex nature of the SPI bus, the SPI master on DaVinci needs transmit to be active even if the tranfer is only meant to collect receive data. The current code achieves this by using a temporary zeroed buffer to provide DMA data in case the transfer does not have a transmit buffer provided. However, the transmit DMA is started only if transmit buffer is provided rendering the temporary buffer unused. Instead the code relies on a write to SPIDAT1 register to trigger transmit operation. This however only sends two bytes of data. Fix this by starting transmit DMA always. This changes exposes a bug on DM355 where the CSHOLD bit in SPIDAT1 needs to be written to in between transfers. Handle that by introducing a "cshold_bug" platform data which is set to true for DM355. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 4aa522134a90..a5f03dd8d8da 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -719,13 +719,14 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) struct davinci_spi *davinci_spi; int int_status = 0; int count, temp_count; - u32 data1_reg_val; struct davinci_spi_dma *davinci_spi_dma; int data_type, ret; unsigned long tx_reg, rx_reg; + struct davinci_spi_platform_data *pdata; struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); + pdata = davinci_spi->pdata; sdev = davinci_spi->bitbang.master->dev.parent; davinci_spi_dma = &davinci_spi->dma_channels; @@ -739,8 +740,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) /* convert len to words based on bits_per_word */ data_type = davinci_spi->bytes_per_word[spi->chip_select]; - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); - init_completion(&davinci_spi_dma->dma_rx_completion); init_completion(&davinci_spi_dma->dma_tx_completion); @@ -781,9 +780,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) edma_set_dest_index(davinci_spi_dma->dma_tx_channel, 0, 0); if (t->rx_buf) { - /* initiate transaction */ - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); - t->rx_dma = dma_map_single(&spi->dev, (void *)t->rx_buf, count, DMA_FROM_DEVICE); if (dma_mapping_error(&spi->dev, t->rx_dma)) { @@ -805,18 +801,18 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) data_type, 0); } - if ((t->tx_buf) || (t->rx_buf)) - edma_start(davinci_spi_dma->dma_tx_channel); + if (pdata->cshold_bug) { + u16 spidat1 = ioread16(davinci_spi->base + SPIDAT1 + 2); + iowrite16(spidat1, davinci_spi->base + SPIDAT1 + 2); + } if (t->rx_buf) edma_start(davinci_spi_dma->dma_rx_channel); - if ((t->rx_buf) || (t->tx_buf)) - davinci_spi_set_dma_req(spi, 1); + edma_start(davinci_spi_dma->dma_tx_channel); + davinci_spi_set_dma_req(spi, 1); - if (t->tx_buf) - wait_for_completion_interruptible( - &davinci_spi_dma->dma_tx_completion); + wait_for_completion_interruptible(&davinci_spi_dma->dma_tx_completion); if (t->rx_buf) wait_for_completion_interruptible( -- cgit v1.2.2 From d3f7141cbf4580b2f18f93940df29cf0c15e7ef5 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Wed, 29 Sep 2010 12:31:54 +0530 Subject: spi: davinci: do not use temporary buffer if no transmit data provided Remove usage of temporary buffer when no transmit data is provided. Instead, use the transmit register itself as the source of data. By choosing the transmit register itself as the source of data, this patch helps remove unnecessary accesses to memory when no real data is being transmitted. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 52 +++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 31 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index a5f03dd8d8da..f5129390bc2d 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -38,8 +38,6 @@ #define CS_DEFAULT 0xFF -#define SPI_BUFSIZ (SMP_CACHE_BYTES + 1) - #define SPIFMT_PHASE_MASK BIT(16) #define SPIFMT_POLARITY_MASK BIT(17) #define SPIFMT_DISTIMER_MASK BIT(18) @@ -140,7 +138,6 @@ struct davinci_spi { const void *tx; void *rx; - u8 *tmp_buf; int rcount; int wcount; struct davinci_spi_dma dma_channels; @@ -718,7 +715,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; int int_status = 0; - int count, temp_count; + int count; struct davinci_spi_dma *davinci_spi_dma; int data_type, ret; unsigned long tx_reg, rx_reg; @@ -750,6 +747,18 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); + /* + * Transmit DMA setup + * + * If there is transmit data, map the transmit buffer, set it as the + * source of data and set the source B index to data size. + * If there is no transmit data, set the transmit register as the + * source of data, and set the source B index to zero. + * + * The destination is always the transmit register itself. And the + * destination never increments. + */ + if (t->tx_buf) { t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, count, DMA_TO_DEVICE); @@ -758,25 +767,15 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) " TX buffer\n", count); return -ENOMEM; } - temp_count = count; - } else { - /* We need TX clocking for RX transaction */ - t->tx_dma = dma_map_single(&spi->dev, - (void *)davinci_spi->tmp_buf, count + 1, - DMA_TO_DEVICE); - if (dma_mapping_error(&spi->dev, t->tx_dma)) { - dev_dbg(sdev, "Unable to DMA map a %d bytes" - " TX tmp buffer\n", count); - return -ENOMEM; - } - temp_count = count + 1; } edma_set_transfer_params(davinci_spi_dma->dma_tx_channel, - data_type, temp_count, 1, 0, ASYNC); + data_type, count, 1, 0, ASYNC); edma_set_dest(davinci_spi_dma->dma_tx_channel, tx_reg, INCR, W8BIT); - edma_set_src(davinci_spi_dma->dma_tx_channel, t->tx_dma, INCR, W8BIT); - edma_set_src_index(davinci_spi_dma->dma_tx_channel, data_type, 0); + edma_set_src(davinci_spi_dma->dma_tx_channel, + t->tx_buf ? t->tx_dma : tx_reg, INCR, W8BIT); + edma_set_src_index(davinci_spi_dma->dma_tx_channel, + t->tx_buf ? data_type : 0, 0); edma_set_dest_index(davinci_spi_dma->dma_tx_channel, 0, 0); if (t->rx_buf) { @@ -818,7 +817,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) wait_for_completion_interruptible( &davinci_spi_dma->dma_rx_completion); - dma_unmap_single(NULL, t->tx_dma, temp_count, DMA_TO_DEVICE); + if (t->tx_buf) + dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); if (t->rx_buf) dma_unmap_single(NULL, t->rx_dma, count, DMA_FROM_DEVICE); @@ -906,17 +906,10 @@ static int davinci_spi_probe(struct platform_device *pdev) if (ret) goto unmap_io; - /* Allocate tmp_buf for tx_buf */ - davinci_spi->tmp_buf = kzalloc(SPI_BUFSIZ, GFP_KERNEL); - if (davinci_spi->tmp_buf == NULL) { - ret = -ENOMEM; - goto irq_free; - } - davinci_spi->bitbang.master = spi_master_get(master); if (davinci_spi->bitbang.master == NULL) { ret = -ENODEV; - goto free_tmp_buf; + goto irq_free; } davinci_spi->clk = clk_get(&pdev->dev, NULL); @@ -1027,8 +1020,6 @@ free_clk: clk_put(davinci_spi->clk); put_master: spi_master_put(master); -free_tmp_buf: - kfree(davinci_spi->tmp_buf); irq_free: free_irq(davinci_spi->irq, davinci_spi); unmap_io: @@ -1063,7 +1054,6 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) clk_disable(davinci_spi->clk); clk_put(davinci_spi->clk); spi_master_put(master); - kfree(davinci_spi->tmp_buf); free_irq(davinci_spi->irq, davinci_spi); iounmap(davinci_spi->base); release_mem_region(davinci_spi->pbase, davinci_spi->region_size); -- cgit v1.2.2 From e91c659bbcf2f47519260182a75f64ede34df3ca Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 1 Oct 2010 10:29:29 +0530 Subject: spi: davinci: always start receive DMA In keeping with the full duplex nature of the SPI bus. Always start receive DMA along with transmit DMA. If there is no receive buffer provided with the transfer, use a temporary buffer to receive the data to be thrown away. [michael.williamson@criticallink.com: receive DMA size should be same as transfer length to avoid hang-up when transfer length is smaller than temporary rx buffer size (rx buffer not provided)] Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 70 ++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 28 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index f5129390bc2d..705d0069bce0 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -138,6 +138,8 @@ struct davinci_spi { const void *tx; void *rx; +#define SPI_TMP_BUFSZ (SMP_CACHE_BYTES + 1) + u8 rx_tmp_buf[SPI_TMP_BUFSZ]; int rcount; int wcount; struct davinci_spi_dma dma_channels; @@ -716,10 +718,12 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) struct davinci_spi *davinci_spi; int int_status = 0; int count; + unsigned rx_buf_count; struct davinci_spi_dma *davinci_spi_dma; int data_type, ret; unsigned long tx_reg, rx_reg; struct davinci_spi_platform_data *pdata; + void *rx_buf; struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); @@ -778,50 +782,60 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) t->tx_buf ? data_type : 0, 0); edma_set_dest_index(davinci_spi_dma->dma_tx_channel, 0, 0); + /* + * Receive DMA setup + * + * If there is receive buffer, use it to receive data. If there + * is none provided, use a temporary receive buffer. Set the + * destination B index to 0 so effectively only one byte is used + * in the temporary buffer (address does not increment). + * + * The source of receive data is the receive data register. The + * source address never increments. + */ + if (t->rx_buf) { - t->rx_dma = dma_map_single(&spi->dev, (void *)t->rx_buf, count, - DMA_FROM_DEVICE); - if (dma_mapping_error(&spi->dev, t->rx_dma)) { - dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", - count); - if (t->tx_buf != NULL) - dma_unmap_single(NULL, t->tx_dma, - count, DMA_TO_DEVICE); - return -ENOMEM; - } - edma_set_transfer_params(davinci_spi_dma->dma_rx_channel, - data_type, count, 1, 0, ASYNC); - edma_set_src(davinci_spi_dma->dma_rx_channel, - rx_reg, INCR, W8BIT); - edma_set_dest(davinci_spi_dma->dma_rx_channel, - t->rx_dma, INCR, W8BIT); - edma_set_src_index(davinci_spi_dma->dma_rx_channel, 0, 0); - edma_set_dest_index(davinci_spi_dma->dma_rx_channel, - data_type, 0); + rx_buf = t->rx_buf; + rx_buf_count = count; + } else { + rx_buf = davinci_spi->rx_tmp_buf; + rx_buf_count = sizeof(davinci_spi->rx_tmp_buf); + } + + t->rx_dma = dma_map_single(&spi->dev, rx_buf, rx_buf_count, + DMA_FROM_DEVICE); + if (dma_mapping_error(&spi->dev, t->rx_dma)) { + dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", + rx_buf_count); + if (t->tx_buf) + dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); + return -ENOMEM; } + edma_set_transfer_params(davinci_spi_dma->dma_rx_channel, data_type, + count, 1, 0, ASYNC); + edma_set_src(davinci_spi_dma->dma_rx_channel, rx_reg, INCR, W8BIT); + edma_set_dest(davinci_spi_dma->dma_rx_channel, t->rx_dma, INCR, W8BIT); + edma_set_src_index(davinci_spi_dma->dma_rx_channel, 0, 0); + edma_set_dest_index(davinci_spi_dma->dma_rx_channel, + t->rx_buf ? data_type : 0, 0); + if (pdata->cshold_bug) { u16 spidat1 = ioread16(davinci_spi->base + SPIDAT1 + 2); iowrite16(spidat1, davinci_spi->base + SPIDAT1 + 2); } - if (t->rx_buf) - edma_start(davinci_spi_dma->dma_rx_channel); - + edma_start(davinci_spi_dma->dma_rx_channel); edma_start(davinci_spi_dma->dma_tx_channel); davinci_spi_set_dma_req(spi, 1); wait_for_completion_interruptible(&davinci_spi_dma->dma_tx_completion); - - if (t->rx_buf) - wait_for_completion_interruptible( - &davinci_spi_dma->dma_rx_completion); + wait_for_completion_interruptible(&davinci_spi_dma->dma_rx_completion); if (t->tx_buf) dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); - if (t->rx_buf) - dma_unmap_single(NULL, t->rx_dma, count, DMA_FROM_DEVICE); + dma_unmap_single(NULL, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); /* * Check for bit error, desync error,parity error,timeout error and -- cgit v1.2.2 From 49fc3f497d7d409e9b0dc384fe7c173bccd3b1a1 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 1 Oct 2010 11:22:23 +0530 Subject: spi: davinci: use edma_write_slot() to setup EDMA PaRAM slot Currently a series of EDMA API calls are being made to setup various aspects of EDMA PaRAM slots for receive and transmit. Instead setup the PaRAM using a local structure and write once to the hardware using edma_write_slot() Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 705d0069bce0..67f1e463c1f0 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -725,6 +725,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) struct davinci_spi_platform_data *pdata; void *rx_buf; struct device *sdev; + struct edmacc_param param; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; @@ -773,14 +774,15 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) } } - edma_set_transfer_params(davinci_spi_dma->dma_tx_channel, - data_type, count, 1, 0, ASYNC); - edma_set_dest(davinci_spi_dma->dma_tx_channel, tx_reg, INCR, W8BIT); - edma_set_src(davinci_spi_dma->dma_tx_channel, - t->tx_buf ? t->tx_dma : tx_reg, INCR, W8BIT); - edma_set_src_index(davinci_spi_dma->dma_tx_channel, - t->tx_buf ? data_type : 0, 0); - edma_set_dest_index(davinci_spi_dma->dma_tx_channel, 0, 0); + param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_tx_channel); + param.src = t->tx_buf ? t->tx_dma : tx_reg; + param.a_b_cnt = count << 16 | data_type; + param.dst = tx_reg; + param.src_dst_bidx = t->tx_buf ? data_type : 0; + param.link_bcntrld = 0xffff; + param.src_dst_cidx = 0; + param.ccnt = 1; + edma_write_slot(davinci_spi_dma->dma_tx_channel, ¶m); /* * Receive DMA setup @@ -812,13 +814,15 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) return -ENOMEM; } - edma_set_transfer_params(davinci_spi_dma->dma_rx_channel, data_type, - count, 1, 0, ASYNC); - edma_set_src(davinci_spi_dma->dma_rx_channel, rx_reg, INCR, W8BIT); - edma_set_dest(davinci_spi_dma->dma_rx_channel, t->rx_dma, INCR, W8BIT); - edma_set_src_index(davinci_spi_dma->dma_rx_channel, 0, 0); - edma_set_dest_index(davinci_spi_dma->dma_rx_channel, - t->rx_buf ? data_type : 0, 0); + param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_rx_channel); + param.src = rx_reg; + param.a_b_cnt = count << 16 | data_type; + param.dst = t->rx_dma; + param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; + param.link_bcntrld = 0xffff; + param.src_dst_cidx = 0; + param.ccnt = 1; + edma_write_slot(davinci_spi_dma->dma_rx_channel, ¶m); if (pdata->cshold_bug) { u16 spidat1 = ioread16(davinci_spi->base + SPIDAT1 + 2); -- cgit v1.2.2 From a4f4497b86a689aa8c827d4ebe0d00c4eba66f76 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 1 Oct 2010 14:00:48 +0530 Subject: spi: davinci: fix DMA event generation stoppage Do not stop SPI DMA event generation in either transmit or receive DMA event call back because the single setting affects both transmit and receive event generation. Depending on the order in which the callbacks happen, transmit or receive events can get unintentionally stalled. Instead, disable event generation once after both the transmit and receive DMA completes. While at it, remove the largely under-used function to set or clear DMA event generation. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 67f1e463c1f0..9695f98b03bd 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -211,16 +211,6 @@ static inline void clear_io_bits(void __iomem *addr, u32 bits) iowrite32(v, addr); } -static void davinci_spi_set_dma_req(const struct spi_device *spi, int enable) -{ - struct davinci_spi *davinci_spi = spi_master_get_devdata(spi->master); - - if (enable) - set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); - else - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); -} - /* * Interface to control the chip select signal */ @@ -414,8 +404,6 @@ static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) edma_clean_channel(davinci_spi_dma->dma_rx_channel); complete(&davinci_spi_dma->dma_rx_completion); - /* We must disable the DMA RX request */ - davinci_spi_set_dma_req(spi, 0); } static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) @@ -433,8 +421,6 @@ static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) edma_clean_channel(davinci_spi_dma->dma_tx_channel); complete(&davinci_spi_dma->dma_tx_completion); - /* We must disable the DMA TX request */ - davinci_spi_set_dma_req(spi, 0); } static int davinci_spi_request_dma(struct spi_device *spi) @@ -831,7 +817,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) edma_start(davinci_spi_dma->dma_rx_channel); edma_start(davinci_spi_dma->dma_tx_channel); - davinci_spi_set_dma_req(spi, 1); + set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); wait_for_completion_interruptible(&davinci_spi_dma->dma_tx_completion); wait_for_completion_interruptible(&davinci_spi_dma->dma_rx_completion); @@ -841,6 +827,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) dma_unmap_single(NULL, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); + clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); + /* * Check for bit error, desync error,parity error,timeout error and * receive overflow errors -- cgit v1.2.2 From 903ca25b219e28e3513ca4c2ff379fcdf19e057e Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Fri, 1 Oct 2010 14:51:40 +0530 Subject: spi: davinci: do not allocate DMA channels during SPI device setup Do not allocate (and de-allocate) SPI DMA channels during setup (and cleanup) for each SPI device. Instead, allocate the DMA channels once duing probe and use them for the life time of the driver. This makes sense since there are dedicated DMA channels meant for SPI use. This also helps remove the unnecessary DMA "sync_dev" variables being used to store DMA channel information. Also, the "use_dma" platform variable is now eliminated since it is possible to check if the platform supports DMA or not based upon whether DMA resources can be found or not. Tested-By: Michael Williamson Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 153 ++++++++++++++++------------------------------ 1 file changed, 51 insertions(+), 102 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 9695f98b03bd..6db478601024 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -116,8 +116,6 @@ struct davinci_spi_dma { int dma_tx_channel; int dma_rx_channel; - int dma_tx_sync_dev; - int dma_rx_sync_dev; enum dma_event_q eventq; struct completion dma_tx_completion; @@ -153,8 +151,6 @@ struct davinci_spi { static struct davinci_spi_config davinci_spi_default_cfg; -static unsigned use_dma; - static void davinci_spi_rx_buf_u8(u32 data, struct davinci_spi *davinci_spi) { if (davinci_spi->rx) { @@ -391,12 +387,7 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) { - struct spi_device *spi = (struct spi_device *)data; - struct davinci_spi *davinci_spi; - struct davinci_spi_dma *davinci_spi_dma; - - davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &davinci_spi->dma_channels; + struct davinci_spi_dma *davinci_spi_dma = data; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_rx_channel); @@ -408,12 +399,7 @@ static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) { - struct spi_device *spi = (struct spi_device *)data; - struct davinci_spi *davinci_spi; - struct davinci_spi_dma *davinci_spi_dma; - - davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &davinci_spi->dma_channels; + struct davinci_spi_dma *davinci_spi_dma = data; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_tx_channel); @@ -423,39 +409,6 @@ static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) complete(&davinci_spi_dma->dma_tx_completion); } -static int davinci_spi_request_dma(struct spi_device *spi) -{ - struct davinci_spi *davinci_spi; - struct davinci_spi_dma *davinci_spi_dma; - struct device *sdev; - int r; - - davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &davinci_spi->dma_channels; - sdev = davinci_spi->bitbang.master->dev.parent; - - r = edma_alloc_channel(davinci_spi_dma->dma_rx_sync_dev, - davinci_spi_dma_rx_callback, spi, - davinci_spi_dma->eventq); - if (r < 0) { - dev_dbg(sdev, "Unable to request DMA channel for SPI RX\n"); - return -EAGAIN; - } - davinci_spi_dma->dma_rx_channel = r; - r = edma_alloc_channel(davinci_spi_dma->dma_tx_sync_dev, - davinci_spi_dma_tx_callback, spi, - davinci_spi_dma->eventq); - if (r < 0) { - edma_free_channel(davinci_spi_dma->dma_rx_channel); - davinci_spi_dma->dma_rx_channel = -1; - dev_dbg(sdev, "Unable to request DMA channel for SPI TX\n"); - return -EAGAIN; - } - davinci_spi_dma->dma_tx_channel = r; - - return 0; -} - /** * davinci_spi_setup - This functions will set default transfer method * @spi: spi device on which data transfer to be done @@ -466,7 +419,6 @@ static int davinci_spi_setup(struct spi_device *spi) { int retval = 0; struct davinci_spi *davinci_spi; - struct davinci_spi_dma *davinci_spi_dma; struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); @@ -494,33 +446,9 @@ static int davinci_spi_setup(struct spi_device *spi) clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_LOOPBACK_MASK); - if (use_dma) { - davinci_spi_dma = &davinci_spi->dma_channels; - - if ((davinci_spi_dma->dma_rx_channel == -1) || - (davinci_spi_dma->dma_tx_channel == -1)) - retval = davinci_spi_request_dma(spi); - } - return retval; } -static void davinci_spi_cleanup(struct spi_device *spi) -{ - if (use_dma) { - struct davinci_spi *davinci_spi = - spi_master_get_devdata(spi->master); - struct davinci_spi_dma *davinci_spi_dma = - &davinci_spi->dma_channels; - - if ((davinci_spi_dma->dma_rx_channel != -1) - && (davinci_spi_dma->dma_tx_channel != -1)) { - edma_free_channel(davinci_spi_dma->dma_tx_channel); - edma_free_channel(davinci_spi_dma->dma_rx_channel); - } - } -} - static int davinci_spi_check_error(struct davinci_spi *davinci_spi, int int_status) { @@ -842,6 +770,30 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) return t->len; } +static int davinci_spi_request_dma(struct davinci_spi_dma *davinci_spi_dma) +{ + int r; + + r = edma_alloc_channel(davinci_spi_dma->dma_rx_channel, + davinci_spi_dma_rx_callback, davinci_spi_dma, + davinci_spi_dma->eventq); + if (r < 0) { + pr_err("Unable to request DMA channel for SPI RX\n"); + return -EAGAIN; + } + + r = edma_alloc_channel(davinci_spi_dma->dma_tx_channel, + davinci_spi_dma_tx_callback, davinci_spi_dma, + davinci_spi_dma->eventq); + if (r < 0) { + edma_free_channel(davinci_spi_dma->dma_rx_channel); + pr_err("Unable to request DMA channel for SPI TX\n"); + return -EAGAIN; + } + + return 0; +} + /** * davinci_spi_probe - probe function for SPI Master Controller * @pdev: platform_device structure which contains plateform specific data @@ -928,45 +880,39 @@ static int davinci_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = pdata->num_chipselect; master->setup = davinci_spi_setup; - master->cleanup = davinci_spi_cleanup; davinci_spi->bitbang.chipselect = davinci_spi_chipselect; davinci_spi->bitbang.setup_transfer = davinci_spi_setup_transfer; davinci_spi->version = pdata->version; - use_dma = pdata->use_dma; davinci_spi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP; if (davinci_spi->version == SPI_VERSION_2) davinci_spi->bitbang.flags |= SPI_READY; - if (use_dma) { - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (r) - dma_rx_chan = r->start; - r = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (r) - dma_tx_chan = r->start; - r = platform_get_resource(pdev, IORESOURCE_DMA, 2); - if (r) - dma_eventq = r->start; - } - - if (!use_dma || - dma_rx_chan == SPI_NO_RESOURCE || - dma_tx_chan == SPI_NO_RESOURCE || - dma_eventq == SPI_NO_RESOURCE) { - davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_pio; - use_dma = 0; - } else { - davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_dma; - - davinci_spi->dma_channels.dma_rx_channel = -1; - davinci_spi->dma_channels.dma_rx_sync_dev = dma_rx_chan; - davinci_spi->dma_channels.dma_tx_channel = -1; - davinci_spi->dma_channels.dma_tx_sync_dev = dma_tx_chan; + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r) + dma_rx_chan = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (r) + dma_tx_chan = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 2); + if (r) + dma_eventq = r->start; + + davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_pio; + if (dma_rx_chan != SPI_NO_RESOURCE && + dma_tx_chan != SPI_NO_RESOURCE && + dma_eventq != SPI_NO_RESOURCE) { + davinci_spi->dma_channels.dma_rx_channel = dma_rx_chan; + davinci_spi->dma_channels.dma_tx_channel = dma_tx_chan; davinci_spi->dma_channels.eventq = dma_eventq; + ret = davinci_spi_request_dma(&davinci_spi->dma_channels); + if (ret) + goto free_clk; + + davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_dma; dev_info(&pdev->dev, "DaVinci SPI driver in EDMA mode\n" "Using RX channel = %d , TX channel = %d and " "event queue = %d", dma_rx_chan, dma_tx_chan, @@ -1015,12 +961,15 @@ static int davinci_spi_probe(struct platform_device *pdev) ret = spi_bitbang_start(&davinci_spi->bitbang); if (ret) - goto free_clk; + goto free_dma; dev_info(&pdev->dev, "Controller at 0x%p\n", davinci_spi->base); return ret; +free_dma: + edma_free_channel(davinci_spi->dma_channels.dma_tx_channel); + edma_free_channel(davinci_spi->dma_channels.dma_rx_channel); free_clk: clk_disable(davinci_spi->clk); clk_put(davinci_spi->clk); -- cgit v1.2.2 From 523c37e7006522e778a1fd0aea2746ceb788572f Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Mon, 4 Oct 2010 17:35:34 +0530 Subject: spi: davinci: fix EDMA CC errors at end of transfers Use a dummy param slot linked to itself to take care of the extra "sync event" that gets sent to EDMA controller after the last byte has been transferred. The dummy PaRAM slot that is linked to the actual DMA PaRAM slot "absorbs" this event and prevents a EDMA CC error to be asserted. Without this provision, the EDMA CC error would be asserted because the channel PaRAM would be empty after the transfer and EDMA would not know what to make out of the extra sync event. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 6db478601024..975c2a228d0a 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -116,6 +116,7 @@ struct davinci_spi_dma { int dma_tx_channel; int dma_rx_channel; + int dummy_param_slot; enum dma_event_q eventq; struct completion dma_tx_completion; @@ -697,6 +698,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) param.src_dst_cidx = 0; param.ccnt = 1; edma_write_slot(davinci_spi_dma->dma_tx_channel, ¶m); + edma_link(davinci_spi_dma->dma_tx_channel, + davinci_spi_dma->dummy_param_slot); /* * Receive DMA setup @@ -779,19 +782,37 @@ static int davinci_spi_request_dma(struct davinci_spi_dma *davinci_spi_dma) davinci_spi_dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI RX\n"); - return -EAGAIN; + r = -EAGAIN; + goto rx_dma_failed; } r = edma_alloc_channel(davinci_spi_dma->dma_tx_channel, davinci_spi_dma_tx_callback, davinci_spi_dma, davinci_spi_dma->eventq); if (r < 0) { - edma_free_channel(davinci_spi_dma->dma_rx_channel); pr_err("Unable to request DMA channel for SPI TX\n"); - return -EAGAIN; + r = -EAGAIN; + goto tx_dma_failed; } + r = edma_alloc_slot(EDMA_CTLR(davinci_spi_dma->dma_tx_channel), + EDMA_SLOT_ANY); + if (r < 0) { + pr_err("Unable to request SPI TX DMA param slot\n"); + r = -EAGAIN; + goto param_failed; + } + davinci_spi_dma->dummy_param_slot = r; + edma_link(davinci_spi_dma->dummy_param_slot, + davinci_spi_dma->dummy_param_slot); + return 0; +param_failed: + edma_free_channel(davinci_spi_dma->dma_tx_channel); +tx_dma_failed: + edma_free_channel(davinci_spi_dma->dma_rx_channel); +rx_dma_failed: + return r; } /** @@ -970,6 +991,7 @@ static int davinci_spi_probe(struct platform_device *pdev) free_dma: edma_free_channel(davinci_spi->dma_channels.dma_tx_channel); edma_free_channel(davinci_spi->dma_channels.dma_rx_channel); + edma_free_slot(davinci_spi->dma_channels.dummy_param_slot); free_clk: clk_disable(davinci_spi->clk); clk_put(davinci_spi->clk); -- cgit v1.2.2 From 9b189fd7584a1d8c68334dd1bc47b363877b314e Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Tue, 5 Oct 2010 11:38:41 +0530 Subject: spi: davinci: handle DMA completion errors correctly Do not simply clean the DMA channel on a DMA completion error. Instead, use wcount and rcount members of davinci_spi to detecion non-completion of DMA and signal EIO to the application. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 63 +++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 27 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 975c2a228d0a..a47947da17a3 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -388,24 +388,26 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) { - struct davinci_spi_dma *davinci_spi_dma = data; + struct davinci_spi *davinci_spi = data; + struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + + edma_stop(davinci_spi_dma->dma_rx_channel); if (ch_status == DMA_COMPLETE) - edma_stop(davinci_spi_dma->dma_rx_channel); - else - edma_clean_channel(davinci_spi_dma->dma_rx_channel); + davinci_spi->rcount = 0; complete(&davinci_spi_dma->dma_rx_completion); } static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) { - struct davinci_spi_dma *davinci_spi_dma = data; + struct davinci_spi *davinci_spi = data; + struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + + edma_stop(davinci_spi_dma->dma_tx_channel); if (ch_status == DMA_COMPLETE) - edma_stop(davinci_spi_dma->dma_tx_channel); - else - edma_clean_channel(davinci_spi_dma->dma_tx_channel); + davinci_spi->wcount = 0; complete(&davinci_spi_dma->dma_tx_completion); } @@ -632,7 +634,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; int int_status = 0; - int count; unsigned rx_buf_count; struct davinci_spi_dma *davinci_spi_dma; int data_type, ret; @@ -648,20 +649,20 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi_dma = &davinci_spi->dma_channels; + /* convert len to words based on bits_per_word */ + data_type = davinci_spi->bytes_per_word[spi->chip_select]; + tx_reg = (unsigned long)davinci_spi->pbase + SPIDAT1; rx_reg = (unsigned long)davinci_spi->pbase + SPIBUF; davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; - - /* convert len to words based on bits_per_word */ - data_type = davinci_spi->bytes_per_word[spi->chip_select]; + davinci_spi->wcount = t->len / data_type; + davinci_spi->rcount = davinci_spi->wcount; init_completion(&davinci_spi_dma->dma_rx_completion); init_completion(&davinci_spi_dma->dma_tx_completion); - count = t->len / data_type; /* the number of elements */ - /* disable all interrupts for dma transfers */ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); /* Enable SPI */ @@ -680,18 +681,18 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) */ if (t->tx_buf) { - t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, count, - DMA_TO_DEVICE); + t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, + davinci_spi->wcount, DMA_TO_DEVICE); if (dma_mapping_error(&spi->dev, t->tx_dma)) { - dev_dbg(sdev, "Unable to DMA map a %d bytes" - " TX buffer\n", count); + dev_dbg(sdev, "Unable to DMA map %d bytes TX buffer\n", + davinci_spi->wcount); return -ENOMEM; } } param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_tx_channel); param.src = t->tx_buf ? t->tx_dma : tx_reg; - param.a_b_cnt = count << 16 | data_type; + param.a_b_cnt = davinci_spi->wcount << 16 | data_type; param.dst = tx_reg; param.src_dst_bidx = t->tx_buf ? data_type : 0; param.link_bcntrld = 0xffff; @@ -715,7 +716,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) if (t->rx_buf) { rx_buf = t->rx_buf; - rx_buf_count = count; + rx_buf_count = davinci_spi->rcount; } else { rx_buf = davinci_spi->rx_tmp_buf; rx_buf_count = sizeof(davinci_spi->rx_tmp_buf); @@ -727,13 +728,14 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", rx_buf_count); if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); + dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, + DMA_TO_DEVICE); return -ENOMEM; } param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_rx_channel); param.src = rx_reg; - param.a_b_cnt = count << 16 | data_type; + param.a_b_cnt = davinci_spi->rcount << 16 | data_type; param.dst = t->rx_dma; param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; param.link_bcntrld = 0xffff; @@ -754,7 +756,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) wait_for_completion_interruptible(&davinci_spi_dma->dma_rx_completion); if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); + dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, + DMA_TO_DEVICE); dma_unmap_single(NULL, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); @@ -770,15 +773,21 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) if (ret != 0) return ret; + if (davinci_spi->rcount != 0 || davinci_spi->wcount != 0) { + dev_err(sdev, "SPI data transfer error\n"); + return -EIO; + } + return t->len; } -static int davinci_spi_request_dma(struct davinci_spi_dma *davinci_spi_dma) +static int davinci_spi_request_dma(struct davinci_spi *davinci_spi) { int r; + struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; r = edma_alloc_channel(davinci_spi_dma->dma_rx_channel, - davinci_spi_dma_rx_callback, davinci_spi_dma, + davinci_spi_dma_rx_callback, davinci_spi, davinci_spi_dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI RX\n"); @@ -787,7 +796,7 @@ static int davinci_spi_request_dma(struct davinci_spi_dma *davinci_spi_dma) } r = edma_alloc_channel(davinci_spi_dma->dma_tx_channel, - davinci_spi_dma_tx_callback, davinci_spi_dma, + davinci_spi_dma_tx_callback, davinci_spi, davinci_spi_dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI TX\n"); @@ -929,7 +938,7 @@ static int davinci_spi_probe(struct platform_device *pdev) davinci_spi->dma_channels.dma_tx_channel = dma_tx_chan; davinci_spi->dma_channels.eventq = dma_eventq; - ret = davinci_spi_request_dma(&davinci_spi->dma_channels); + ret = davinci_spi_request_dma(davinci_spi); if (ret) goto free_clk; -- cgit v1.2.2 From 6dbd29b27bd2627ba0025a6cff14381e69512cdf Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Tue, 5 Oct 2010 15:43:08 +0530 Subject: spi: davinci: remove usage of additional completion variables for DMA The DMA code does not use the existing completion variable 'done' which is being used for interrupt mode transfers. Instead it uses two different completion variables specific to DMA mode transfers. Eliminate the usage of new completion variables for DMA mode and use the existing completion variable. [nsekhar@ti.com: To make this process easy, eliminate the two different DMA completion callback functions for tx and rx and use a single callback function instead] Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 58 +++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index a47947da17a3..6094e3a07853 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -118,9 +118,6 @@ struct davinci_spi_dma { int dma_rx_channel; int dummy_param_slot; enum dma_event_q eventq; - - struct completion dma_tx_completion; - struct completion dma_rx_completion; }; /* SPI Controller driver's private data. */ @@ -386,32 +383,6 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, return 0; } -static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) -{ - struct davinci_spi *davinci_spi = data; - struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; - - edma_stop(davinci_spi_dma->dma_rx_channel); - - if (ch_status == DMA_COMPLETE) - davinci_spi->rcount = 0; - - complete(&davinci_spi_dma->dma_rx_completion); -} - -static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) -{ - struct davinci_spi *davinci_spi = data; - struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; - - edma_stop(davinci_spi_dma->dma_tx_channel); - - if (ch_status == DMA_COMPLETE) - davinci_spi->wcount = 0; - - complete(&davinci_spi_dma->dma_tx_completion); -} - /** * davinci_spi_setup - This functions will set default transfer method * @spi: spi device on which data transfer to be done @@ -630,6 +601,25 @@ static irqreturn_t davinci_spi_irq(s32 irq, void *context_data) return IRQ_HANDLED; } +static void davinci_spi_dma_callback(unsigned lch, u16 status, void *data) +{ + struct davinci_spi *davinci_spi = data; + struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + + edma_stop(lch); + + if (status == DMA_COMPLETE) { + if (lch == davinci_spi_dma->dma_rx_channel) + davinci_spi->rcount = 0; + if (lch == davinci_spi_dma->dma_tx_channel) + davinci_spi->wcount = 0; + } + + if ((!davinci_spi->wcount && !davinci_spi->rcount) || + (status != DMA_COMPLETE)) + complete(&davinci_spi->done); +} + static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; @@ -660,8 +650,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi->wcount = t->len / data_type; davinci_spi->rcount = davinci_spi->wcount; - init_completion(&davinci_spi_dma->dma_rx_completion); - init_completion(&davinci_spi_dma->dma_tx_completion); + INIT_COMPLETION(davinci_spi->done); /* disable all interrupts for dma transfers */ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); @@ -752,8 +741,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) edma_start(davinci_spi_dma->dma_tx_channel); set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); - wait_for_completion_interruptible(&davinci_spi_dma->dma_tx_completion); - wait_for_completion_interruptible(&davinci_spi_dma->dma_rx_completion); + wait_for_completion_interruptible(&davinci_spi->done); if (t->tx_buf) dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, @@ -787,7 +775,7 @@ static int davinci_spi_request_dma(struct davinci_spi *davinci_spi) struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; r = edma_alloc_channel(davinci_spi_dma->dma_rx_channel, - davinci_spi_dma_rx_callback, davinci_spi, + davinci_spi_dma_callback, davinci_spi, davinci_spi_dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI RX\n"); @@ -796,7 +784,7 @@ static int davinci_spi_request_dma(struct davinci_spi *davinci_spi) } r = edma_alloc_channel(davinci_spi_dma->dma_tx_channel, - davinci_spi_dma_tx_callback, davinci_spi, + davinci_spi_dma_callback, davinci_spi, davinci_spi_dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI TX\n"); -- cgit v1.2.2 From 87467bd9052725283b9a9f4b1b310fed8744fb1e Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Wed, 6 Oct 2010 17:03:10 +0530 Subject: spi: davinci: let DMA operation be specified on per-device basis Let DMA operation be specified on a per-device basis instead of selecting it once during probe. A side effect of this is the need to combine the PIO and DMA buffer txrx_bufs routine. This is good since they anyway share some common functionality. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 342 +++++++++++++++++++++------------------------- 1 file changed, 155 insertions(+), 187 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 6094e3a07853..5fe298099a1a 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -500,6 +500,25 @@ out: return errors; } +static void davinci_spi_dma_callback(unsigned lch, u16 status, void *data) +{ + struct davinci_spi *davinci_spi = data; + struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + + edma_stop(lch); + + if (status == DMA_COMPLETE) { + if (lch == davinci_spi_dma->dma_rx_channel) + davinci_spi->rcount = 0; + if (lch == davinci_spi_dma->dma_tx_channel) + davinci_spi->wcount = 0; + } + + if ((!davinci_spi->wcount && !davinci_spi->rcount) || + (status != DMA_COMPLETE)) + complete(&davinci_spi->done); +} + /** * davinci_spi_bufs - functions which will handle transfer data * @spi: spi device on which data transfer to be done @@ -509,25 +528,30 @@ out: * of SPI controller and then wait until the completion will be marked * by the IRQ Handler. */ -static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) +static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; - int ret; + int data_type, ret; u32 tx_data, data1_reg_val; u32 errors = 0; struct davinci_spi_config *spicfg; struct davinci_spi_platform_data *pdata; + unsigned uninitialized_var(rx_buf_count); + struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; spicfg = (struct davinci_spi_config *)spi->controller_data; if (!spicfg) spicfg = &davinci_spi_default_cfg; + sdev = davinci_spi->bitbang.master->dev.parent; + + /* convert len to words based on bits_per_word */ + data_type = davinci_spi->bytes_per_word[spi->chip_select]; davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; - davinci_spi->wcount = t->len / - davinci_spi->bytes_per_word[spi->chip_select]; + davinci_spi->wcount = t->len / data_type; davinci_spi->rcount = davinci_spi->wcount; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); @@ -535,20 +559,117 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - if (spicfg->io_type == SPI_IO_TYPE_INTR) { + INIT_COMPLETION(davinci_spi->done); + + if (spicfg->io_type == SPI_IO_TYPE_INTR) set_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKINT); - INIT_COMPLETION(davinci_spi->done); - } - /* start the transfer */ - davinci_spi->wcount--; - tx_data = davinci_spi->get_tx(davinci_spi); - data1_reg_val &= 0xFFFF0000; - data1_reg_val |= tx_data & 0xFFFF; - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + if (spicfg->io_type != SPI_IO_TYPE_DMA) { + /* start the transfer */ + davinci_spi->wcount--; + tx_data = davinci_spi->get_tx(davinci_spi); + data1_reg_val &= 0xFFFF0000; + data1_reg_val |= tx_data & 0xFFFF; + iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + } else { + struct davinci_spi_dma *davinci_spi_dma; + unsigned long tx_reg, rx_reg; + struct edmacc_param param; + void *rx_buf; + + davinci_spi_dma = &davinci_spi->dma_channels; + + tx_reg = (unsigned long)davinci_spi->pbase + SPIDAT1; + rx_reg = (unsigned long)davinci_spi->pbase + SPIBUF; + + /* + * Transmit DMA setup + * + * If there is transmit data, map the transmit buffer, set it + * as the source of data and set the source B index to data + * size. If there is no transmit data, set the transmit register + * as the source of data, and set the source B index to zero. + * + * The destination is always the transmit register itself. And + * the destination never increments. + */ + + if (t->tx_buf) { + t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, + davinci_spi->wcount, DMA_TO_DEVICE); + if (dma_mapping_error(&spi->dev, t->tx_dma)) { + dev_dbg(sdev, "Unable to DMA map %d bytes" + "TX buffer\n", + davinci_spi->wcount); + return -ENOMEM; + } + } + + param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_tx_channel); + param.src = t->tx_buf ? t->tx_dma : tx_reg; + param.a_b_cnt = davinci_spi->wcount << 16 | data_type; + param.dst = tx_reg; + param.src_dst_bidx = t->tx_buf ? data_type : 0; + param.link_bcntrld = 0xffff; + param.src_dst_cidx = 0; + param.ccnt = 1; + edma_write_slot(davinci_spi_dma->dma_tx_channel, ¶m); + edma_link(davinci_spi_dma->dma_tx_channel, + davinci_spi_dma->dummy_param_slot); + + /* + * Receive DMA setup + * + * If there is receive buffer, use it to receive data. If there + * is none provided, use a temporary receive buffer. Set the + * destination B index to 0 so effectively only one byte is used + * in the temporary buffer (address does not increment). + * + * The source of receive data is the receive data register. The + * source address never increments. + */ + + if (t->rx_buf) { + rx_buf = t->rx_buf; + rx_buf_count = davinci_spi->rcount; + } else { + rx_buf = davinci_spi->rx_tmp_buf; + rx_buf_count = sizeof(davinci_spi->rx_tmp_buf); + } + + t->rx_dma = dma_map_single(&spi->dev, rx_buf, rx_buf_count, + DMA_FROM_DEVICE); + if (dma_mapping_error(&spi->dev, t->rx_dma)) { + dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", + rx_buf_count); + if (t->tx_buf) + dma_unmap_single(NULL, t->tx_dma, + davinci_spi->wcount, + DMA_TO_DEVICE); + return -ENOMEM; + } + + param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_rx_channel); + param.src = rx_reg; + param.a_b_cnt = davinci_spi->rcount << 16 | data_type; + param.dst = t->rx_dma; + param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; + param.link_bcntrld = 0xffff; + param.src_dst_cidx = 0; + param.ccnt = 1; + edma_write_slot(davinci_spi_dma->dma_rx_channel, ¶m); + + if (pdata->cshold_bug) + iowrite16(data1_reg_val >> 16, + davinci_spi->base + SPIDAT1 + 2); + + edma_start(davinci_spi_dma->dma_rx_channel); + edma_start(davinci_spi_dma->dma_tx_channel); + set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); + } /* Wait for the transfer to complete */ - if (spicfg->io_type == SPI_IO_TYPE_INTR) { + if (spicfg->io_type != SPI_IO_TYPE_POLL) { wait_for_completion_interruptible(&(davinci_spi->done)); } else { while (davinci_spi->rcount > 0 || davinci_spi->wcount > 0) { @@ -560,6 +681,17 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) } clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); + if (spicfg->io_type == SPI_IO_TYPE_DMA) { + + if (t->tx_buf) + dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, + DMA_TO_DEVICE); + + dma_unmap_single(NULL, t->rx_dma, rx_buf_count, + DMA_FROM_DEVICE); + + clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); + } /* * Check for bit error, desync error,parity error,timeout error and @@ -572,6 +704,11 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) return ret; } + if (davinci_spi->rcount != 0 || davinci_spi->wcount != 0) { + dev_err(sdev, "SPI data transfer error\n"); + return -EIO; + } + return t->len; } @@ -601,174 +738,6 @@ static irqreturn_t davinci_spi_irq(s32 irq, void *context_data) return IRQ_HANDLED; } -static void davinci_spi_dma_callback(unsigned lch, u16 status, void *data) -{ - struct davinci_spi *davinci_spi = data; - struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; - - edma_stop(lch); - - if (status == DMA_COMPLETE) { - if (lch == davinci_spi_dma->dma_rx_channel) - davinci_spi->rcount = 0; - if (lch == davinci_spi_dma->dma_tx_channel) - davinci_spi->wcount = 0; - } - - if ((!davinci_spi->wcount && !davinci_spi->rcount) || - (status != DMA_COMPLETE)) - complete(&davinci_spi->done); -} - -static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) -{ - struct davinci_spi *davinci_spi; - int int_status = 0; - unsigned rx_buf_count; - struct davinci_spi_dma *davinci_spi_dma; - int data_type, ret; - unsigned long tx_reg, rx_reg; - struct davinci_spi_platform_data *pdata; - void *rx_buf; - struct device *sdev; - struct edmacc_param param; - - davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; - sdev = davinci_spi->bitbang.master->dev.parent; - - davinci_spi_dma = &davinci_spi->dma_channels; - - /* convert len to words based on bits_per_word */ - data_type = davinci_spi->bytes_per_word[spi->chip_select]; - - tx_reg = (unsigned long)davinci_spi->pbase + SPIDAT1; - rx_reg = (unsigned long)davinci_spi->pbase + SPIBUF; - - davinci_spi->tx = t->tx_buf; - davinci_spi->rx = t->rx_buf; - davinci_spi->wcount = t->len / data_type; - davinci_spi->rcount = davinci_spi->wcount; - - INIT_COMPLETION(davinci_spi->done); - - /* disable all interrupts for dma transfers */ - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); - /* Enable SPI */ - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - - /* - * Transmit DMA setup - * - * If there is transmit data, map the transmit buffer, set it as the - * source of data and set the source B index to data size. - * If there is no transmit data, set the transmit register as the - * source of data, and set the source B index to zero. - * - * The destination is always the transmit register itself. And the - * destination never increments. - */ - - if (t->tx_buf) { - t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, - davinci_spi->wcount, DMA_TO_DEVICE); - if (dma_mapping_error(&spi->dev, t->tx_dma)) { - dev_dbg(sdev, "Unable to DMA map %d bytes TX buffer\n", - davinci_spi->wcount); - return -ENOMEM; - } - } - - param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_tx_channel); - param.src = t->tx_buf ? t->tx_dma : tx_reg; - param.a_b_cnt = davinci_spi->wcount << 16 | data_type; - param.dst = tx_reg; - param.src_dst_bidx = t->tx_buf ? data_type : 0; - param.link_bcntrld = 0xffff; - param.src_dst_cidx = 0; - param.ccnt = 1; - edma_write_slot(davinci_spi_dma->dma_tx_channel, ¶m); - edma_link(davinci_spi_dma->dma_tx_channel, - davinci_spi_dma->dummy_param_slot); - - /* - * Receive DMA setup - * - * If there is receive buffer, use it to receive data. If there - * is none provided, use a temporary receive buffer. Set the - * destination B index to 0 so effectively only one byte is used - * in the temporary buffer (address does not increment). - * - * The source of receive data is the receive data register. The - * source address never increments. - */ - - if (t->rx_buf) { - rx_buf = t->rx_buf; - rx_buf_count = davinci_spi->rcount; - } else { - rx_buf = davinci_spi->rx_tmp_buf; - rx_buf_count = sizeof(davinci_spi->rx_tmp_buf); - } - - t->rx_dma = dma_map_single(&spi->dev, rx_buf, rx_buf_count, - DMA_FROM_DEVICE); - if (dma_mapping_error(&spi->dev, t->rx_dma)) { - dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", - rx_buf_count); - if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, - DMA_TO_DEVICE); - return -ENOMEM; - } - - param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_rx_channel); - param.src = rx_reg; - param.a_b_cnt = davinci_spi->rcount << 16 | data_type; - param.dst = t->rx_dma; - param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; - param.link_bcntrld = 0xffff; - param.src_dst_cidx = 0; - param.ccnt = 1; - edma_write_slot(davinci_spi_dma->dma_rx_channel, ¶m); - - if (pdata->cshold_bug) { - u16 spidat1 = ioread16(davinci_spi->base + SPIDAT1 + 2); - iowrite16(spidat1, davinci_spi->base + SPIDAT1 + 2); - } - - edma_start(davinci_spi_dma->dma_rx_channel); - edma_start(davinci_spi_dma->dma_tx_channel); - set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); - - wait_for_completion_interruptible(&davinci_spi->done); - - if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, - DMA_TO_DEVICE); - - dma_unmap_single(NULL, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); - - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); - - /* - * Check for bit error, desync error,parity error,timeout error and - * receive overflow errors - */ - int_status = ioread32(davinci_spi->base + SPIFLG); - - ret = davinci_spi_check_error(davinci_spi, int_status); - if (ret != 0) - return ret; - - if (davinci_spi->rcount != 0 || davinci_spi->wcount != 0) { - dev_err(sdev, "SPI data transfer error\n"); - return -EIO; - } - - return t->len; -} - static int davinci_spi_request_dma(struct davinci_spi *davinci_spi) { int r; @@ -918,7 +887,7 @@ static int davinci_spi_probe(struct platform_device *pdev) if (r) dma_eventq = r->start; - davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_pio; + davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs; if (dma_rx_chan != SPI_NO_RESOURCE && dma_tx_chan != SPI_NO_RESOURCE && dma_eventq != SPI_NO_RESOURCE) { @@ -930,10 +899,9 @@ static int davinci_spi_probe(struct platform_device *pdev) if (ret) goto free_clk; - davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_dma; - dev_info(&pdev->dev, "DaVinci SPI driver in EDMA mode\n" - "Using RX channel = %d , TX channel = %d and " - "event queue = %d", dma_rx_chan, dma_tx_chan, + dev_info(&pdev->dev, "DMA: supported\n"); + dev_info(&pdev->dev, "DMA: RX channel: %d, TX channel: %d, " + "event queue: %d\n", dma_rx_chan, dma_tx_chan, dma_eventq); } -- cgit v1.2.2 From 3409e408ab0d7171ae81d198110a1f293852959f Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Wed, 6 Oct 2010 18:13:31 +0530 Subject: spi: davinci: remove non-useful "clk_internal" platform data The "clk_internal" platform data member which contols the CLKMOD bit in Global Control Register 1 is not useful since CLKMOD needs be set to 1 *always* to ensure master mode operation. Remove this platform data. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 5fe298099a1a..2e74fcd2f423 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -927,14 +927,6 @@ static int davinci_spi_probe(struct platform_device *pdev) } } - /* Clock internal */ - if (davinci_spi->pdata->clk_internal) - set_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_CLKMOD_MASK); - else - clear_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_CLKMOD_MASK); - if (pdata->intr_line) iowrite32(SPI_INTLVL_1, davinci_spi->base + SPILVL); else @@ -943,6 +935,7 @@ static int davinci_spi_probe(struct platform_device *pdev) iowrite32(CS_DEFAULT, davinci_spi->base + SPIDEF); /* master mode default */ + set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_MASTER_MASK); ret = spi_bitbang_start(&davinci_spi->bitbang); -- cgit v1.2.2 From 3f27b57c1684efbe11fcc9449df898b1d0feb753 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Wed, 6 Oct 2010 18:25:43 +0530 Subject: spi: davinci: enable and power-up SPI only when required Enable SPI only when active transfers are in progress. Keep it in local low power when not in use. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 2e74fcd2f423..1652bba955e2 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -49,7 +49,6 @@ #define SPIFMT_WDELAY_SHIFT 24 #define SPIFMT_PRESCALE_SHIFT 8 - /* SPIPC0 */ #define SPIPC0_DIFUN_MASK BIT(11) /* MISO */ #define SPIPC0_DOFUN_MASK BIT(10) /* MOSI */ @@ -67,6 +66,7 @@ /* SPIGCR1 */ #define SPIGCR1_CLKMOD_MASK BIT(1) #define SPIGCR1_MASTER_MASK BIT(0) +#define SPIGCR1_POWERDOWN_MASK BIT(8) #define SPIGCR1_LOOPBACK_MASK BIT(16) #define SPIGCR1_SPIENA_MASK BIT(24) @@ -556,7 +556,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); - /* Enable SPI */ + clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); INIT_COMPLETION(davinci_spi->done); @@ -693,6 +693,9 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); } + clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); + set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); + /* * Check for bit error, desync error,parity error,timeout error and * receive overflow errors @@ -937,6 +940,7 @@ static int davinci_spi_probe(struct platform_device *pdev) /* master mode default */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_MASTER_MASK); + set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); ret = spi_bitbang_start(&davinci_spi->bitbang); if (ret) -- cgit v1.2.2 From d8c174cdeb6511aa307e6058468b68a9cc3990e4 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Wed, 6 Oct 2010 18:47:16 +0530 Subject: spi: davinci: setup the driver owner Setup the owner member of the platform driver to THIS_MODULE instead of leaving it NULL. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 1652bba955e2..2ec5fd2f2e7b 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -1001,7 +1001,10 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) } static struct platform_driver davinci_spi_driver = { - .driver.name = "spi_davinci", + .driver = { + .name = "spi_davinci", + .owner = THIS_MODULE, + }, .remove = __exit_p(davinci_spi_remove), }; -- cgit v1.2.2 From 035540f6ea2394bdd9675552d31e1125cd0e402e Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Wed, 6 Oct 2010 18:32:40 +0530 Subject: spi: davinci: add additional comments Add comments describing the platform data members and per-chip-select SPI configuration structure. Also, add some comments describing the what happens during the driver probe. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 2ec5fd2f2e7b..f40f1be1528a 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -787,6 +787,13 @@ rx_dma_failed: /** * davinci_spi_probe - probe function for SPI Master Controller * @pdev: platform_device structure which contains plateform specific data + * + * According to Linux Device Model this function will be invoked by Linux + * with platform_device struct which contains the device specific info. + * This function will map the SPI controller's memory, register IRQ, + * Reset SPI controller and setting its registers to default value. + * It will invoke spi_bitbang_start to create work queue so that client driver + * can register transfer method to work queue. */ static int davinci_spi_probe(struct platform_device *pdev) { -- cgit v1.2.2 From 43abb11ba540e21346c6e7ab3211b48928501ea6 Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Wed, 6 Oct 2010 18:34:47 +0530 Subject: spi: davinci: add EF Johnson Technologies copyright Add copyright for EF Johnson Technologies since the driver has been majorly overhauled by Brian. Signed-off-by: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index f40f1be1528a..42314a321d30 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 Texas Instruments. + * Copyright (C) 2010 EF Johnson Technologies * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- cgit v1.2.2 From 0e0eae4d1cdff5fa6608bcc6f6990774dec2527d Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Fri, 8 Oct 2010 14:04:22 +0530 Subject: spi: davinci: remove unnecessary private data member 'region_size' Remove unnecesary private data member 'region_size' being used to store the size of SPI memory region. Instead, get the memory resource size directly from the platform data. Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 42314a321d30..f6f63d058140 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -129,7 +129,6 @@ struct davinci_spi { u8 version; resource_size_t pbase; void __iomem *base; - size_t region_size; u32 irq; struct completion done; @@ -835,17 +834,15 @@ static int davinci_spi_probe(struct platform_device *pdev) } davinci_spi->pbase = r->start; - davinci_spi->region_size = resource_size(r); davinci_spi->pdata = pdata; - mem = request_mem_region(r->start, davinci_spi->region_size, - pdev->name); + mem = request_mem_region(r->start, resource_size(r), pdev->name); if (mem == NULL) { ret = -EBUSY; goto free_master; } - davinci_spi->base = ioremap(r->start, davinci_spi->region_size); + davinci_spi->base = ioremap(r->start, resource_size(r)); if (davinci_spi->base == NULL) { ret = -ENOMEM; goto release_region; @@ -972,7 +969,7 @@ irq_free: unmap_io: iounmap(davinci_spi->base); release_region: - release_mem_region(davinci_spi->pbase, davinci_spi->region_size); + release_mem_region(davinci_spi->pbase, resource_size(r)); free_master: kfree(master); err: @@ -992,6 +989,7 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) { struct davinci_spi *davinci_spi; struct spi_master *master; + struct resource *r; master = dev_get_drvdata(&pdev->dev); davinci_spi = spi_master_get_devdata(master); @@ -1003,7 +1001,8 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) spi_master_put(master); free_irq(davinci_spi->irq, davinci_spi); iounmap(davinci_spi->base); - release_mem_region(davinci_spi->pbase, davinci_spi->region_size); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(davinci_spi->pbase, resource_size(r)); return 0; } -- cgit v1.2.2 From 212d4b69652171d4474bc9dfc08e829737264632 Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Mon, 11 Oct 2010 10:41:39 +0530 Subject: spi: davinci: shorten variable names Shorten names of local variables and structure members where possible. Local variables: * 'davinci_spi' is being renamed 'dspi' * 'davinci_spi_dma' is being renamed 'dma' Structure members: * 'dma_{tx|rx}_channel' is being renamed '{tx|rx}_channel' since the structure containing them is already called 'davinci_spi_dma' * 'davinci_spi_dma' in 'davinci_spi' structure is being renamed 'dma' Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 409 ++++++++++++++++++++++------------------------ 1 file changed, 198 insertions(+), 211 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index f6f63d058140..6beab99bf95b 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -115,8 +115,8 @@ /* We have 2 DMA channels per CS, one for RX and one for TX */ struct davinci_spi_dma { - int dma_tx_channel; - int dma_rx_channel; + int tx_channel; + int rx_channel; int dummy_param_slot; enum dma_event_q eventq; }; @@ -138,7 +138,7 @@ struct davinci_spi { u8 rx_tmp_buf[SPI_TMP_BUFSZ]; int rcount; int wcount; - struct davinci_spi_dma dma_channels; + struct davinci_spi_dma dma; struct davinci_spi_platform_data *pdata; void (*get_rx)(u32 rx_data, struct davinci_spi *); @@ -149,42 +149,42 @@ struct davinci_spi { static struct davinci_spi_config davinci_spi_default_cfg; -static void davinci_spi_rx_buf_u8(u32 data, struct davinci_spi *davinci_spi) +static void davinci_spi_rx_buf_u8(u32 data, struct davinci_spi *dspi) { - if (davinci_spi->rx) { - u8 *rx = davinci_spi->rx; + if (dspi->rx) { + u8 *rx = dspi->rx; *rx++ = (u8)data; - davinci_spi->rx = rx; + dspi->rx = rx; } } -static void davinci_spi_rx_buf_u16(u32 data, struct davinci_spi *davinci_spi) +static void davinci_spi_rx_buf_u16(u32 data, struct davinci_spi *dspi) { - if (davinci_spi->rx) { - u16 *rx = davinci_spi->rx; + if (dspi->rx) { + u16 *rx = dspi->rx; *rx++ = (u16)data; - davinci_spi->rx = rx; + dspi->rx = rx; } } -static u32 davinci_spi_tx_buf_u8(struct davinci_spi *davinci_spi) +static u32 davinci_spi_tx_buf_u8(struct davinci_spi *dspi) { u32 data = 0; - if (davinci_spi->tx) { - const u8 *tx = davinci_spi->tx; + if (dspi->tx) { + const u8 *tx = dspi->tx; data = *tx++; - davinci_spi->tx = tx; + dspi->tx = tx; } return data; } -static u32 davinci_spi_tx_buf_u16(struct davinci_spi *davinci_spi) +static u32 davinci_spi_tx_buf_u16(struct davinci_spi *dspi) { u32 data = 0; - if (davinci_spi->tx) { - const u16 *tx = davinci_spi->tx; + if (dspi->tx) { + const u16 *tx = dspi->tx; data = *tx++; - davinci_spi->tx = tx; + dspi->tx = tx; } return data; } @@ -210,14 +210,14 @@ static inline void clear_io_bits(void __iomem *addr, u32 bits) */ static void davinci_spi_chipselect(struct spi_device *spi, int value) { - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; struct davinci_spi_platform_data *pdata; u8 chip_sel = spi->chip_select; - u16 spidat1_cfg = CS_DEFAULT; + u16 spidat1 = CS_DEFAULT; bool gpio_chipsel = false; - davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; + dspi = spi_master_get_devdata(spi->master); + pdata = dspi->pdata; if (pdata->chip_sel && chip_sel < pdata->num_chipselect && pdata->chip_sel[chip_sel] != SPI_INTERN_CS) @@ -234,11 +234,11 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) gpio_set_value(pdata->chip_sel[chip_sel], 1); } else { if (value == BITBANG_CS_ACTIVE) { - spidat1_cfg |= SPIDAT1_CSHOLD_MASK; - spidat1_cfg &= ~(0x1 << chip_sel); + spidat1 |= SPIDAT1_CSHOLD_MASK; + spidat1 &= ~(0x1 << chip_sel); } - iowrite16(spidat1_cfg, davinci_spi->base + SPIDAT1 + 2); + iowrite16(spidat1, dspi->base + SPIDAT1 + 2); } } @@ -252,12 +252,12 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) * Returns: calculated prescale - 1 for easy programming into SPI registers * or negative error number if valid prescalar cannot be updated. */ -static inline int davinci_spi_get_prescale(struct davinci_spi *davinci_spi, +static inline int davinci_spi_get_prescale(struct davinci_spi *dspi, u32 max_speed_hz) { int ret; - ret = DIV_ROUND_UP(clk_get_rate(davinci_spi->clk), max_speed_hz); + ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz); if (ret < 3 || ret > 256) return -EINVAL; @@ -278,12 +278,12 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; struct davinci_spi_config *spicfg; u8 bits_per_word = 0; u32 hz = 0, spifmt = 0, prescale = 0; - davinci_spi = spi_master_get_devdata(spi->master); + dspi = spi_master_get_devdata(spi->master); spicfg = (struct davinci_spi_config *)spi->controller_data; if (!spicfg) spicfg = &davinci_spi_default_cfg; @@ -302,13 +302,13 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, * 8bit, 16bit or 32bit transfer */ if (bits_per_word <= 8 && bits_per_word >= 2) { - davinci_spi->get_rx = davinci_spi_rx_buf_u8; - davinci_spi->get_tx = davinci_spi_tx_buf_u8; - davinci_spi->bytes_per_word[spi->chip_select] = 1; + dspi->get_rx = davinci_spi_rx_buf_u8; + dspi->get_tx = davinci_spi_tx_buf_u8; + dspi->bytes_per_word[spi->chip_select] = 1; } else if (bits_per_word <= 16 && bits_per_word >= 2) { - davinci_spi->get_rx = davinci_spi_rx_buf_u16; - davinci_spi->get_tx = davinci_spi_tx_buf_u16; - davinci_spi->bytes_per_word[spi->chip_select] = 2; + dspi->get_rx = davinci_spi_rx_buf_u16; + dspi->get_tx = davinci_spi_tx_buf_u16; + dspi->bytes_per_word[spi->chip_select] = 2; } else return -EINVAL; @@ -317,7 +317,7 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, /* Set up SPIFMTn register, unique to this chipselect. */ - prescale = davinci_spi_get_prescale(davinci_spi, hz); + prescale = davinci_spi_get_prescale(dspi, hz); if (prescale < 0) return prescale; @@ -345,7 +345,7 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, * - 4 pin with enable is (SPI_READY | SPI_NO_CS) */ - if (davinci_spi->version == SPI_VERSION_2) { + if (dspi->version == SPI_VERSION_2) { u32 delay = 0; @@ -375,10 +375,10 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, & SPIDELAY_C2EDELAY_MASK; } - iowrite32(delay, davinci_spi->base + SPIDELAY); + iowrite32(delay, dspi->base + SPIDELAY); } - iowrite32(spifmt, davinci_spi->base + SPIFMT0); + iowrite32(spifmt, dspi->base + SPIFMT0); return 0; } @@ -392,11 +392,11 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, static int davinci_spi_setup(struct spi_device *spi) { int retval = 0; - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; struct davinci_spi_platform_data *pdata; - davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; + dspi = spi_master_get_devdata(spi->master); + pdata = dspi->pdata; /* if bits per word length is zero then set it default 8 */ if (!spi->bits_per_word) @@ -405,28 +405,24 @@ static int davinci_spi_setup(struct spi_device *spi) if (!(spi->mode & SPI_NO_CS)) { if ((pdata->chip_sel == NULL) || (pdata->chip_sel[spi->chip_select] == SPI_INTERN_CS)) - set_io_bits(davinci_spi->base + SPIPC0, - 1 << spi->chip_select); + set_io_bits(dspi->base + SPIPC0, 1 << spi->chip_select); } if (spi->mode & SPI_READY) - set_io_bits(davinci_spi->base + SPIPC0, SPIPC0_SPIENA_MASK); + set_io_bits(dspi->base + SPIPC0, SPIPC0_SPIENA_MASK); if (spi->mode & SPI_LOOP) - set_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_LOOPBACK_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_LOOPBACK_MASK); else - clear_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_LOOPBACK_MASK); + clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_LOOPBACK_MASK); return retval; } -static int davinci_spi_check_error(struct davinci_spi *davinci_spi, - int int_status) +static int davinci_spi_check_error(struct davinci_spi *dspi, int int_status) { - struct device *sdev = davinci_spi->bitbang.master->dev.parent; + struct device *sdev = dspi->bitbang.master->dev.parent; if (int_status & SPIFLG_TIMEOUT_MASK) { dev_dbg(sdev, "SPI Time-out Error\n"); @@ -441,7 +437,7 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, return -EIO; } - if (davinci_spi->version == SPI_VERSION_2) { + if (dspi->version == SPI_VERSION_2) { if (int_status & SPIFLG_DLEN_ERR_MASK) { dev_dbg(sdev, "SPI Data Length Error\n"); return -EIO; @@ -465,35 +461,35 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, /** * davinci_spi_process_events - check for and handle any SPI controller events - * @davinci_spi: the controller data + * @dspi: the controller data * * This function will check the SPIFLG register and handle any events that are * detected there */ -static int davinci_spi_process_events(struct davinci_spi *davinci_spi) +static int davinci_spi_process_events(struct davinci_spi *dspi) { - u32 buf, status, errors = 0, data1_reg_val; + u32 buf, status, errors = 0, spidat1; - buf = ioread32(davinci_spi->base + SPIBUF); + buf = ioread32(dspi->base + SPIBUF); - if (davinci_spi->rcount > 0 && !(buf & SPIBUF_RXEMPTY_MASK)) { - davinci_spi->get_rx(buf & 0xFFFF, davinci_spi); - davinci_spi->rcount--; + if (dspi->rcount > 0 && !(buf & SPIBUF_RXEMPTY_MASK)) { + dspi->get_rx(buf & 0xFFFF, dspi); + dspi->rcount--; } - status = ioread32(davinci_spi->base + SPIFLG); + status = ioread32(dspi->base + SPIFLG); if (unlikely(status & SPIFLG_ERROR_MASK)) { errors = status & SPIFLG_ERROR_MASK; goto out; } - if (davinci_spi->wcount > 0 && !(buf & SPIBUF_TXFULL_MASK)) { - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); - davinci_spi->wcount--; - data1_reg_val &= ~0xFFFF; - data1_reg_val |= 0xFFFF & davinci_spi->get_tx(davinci_spi); - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + if (dspi->wcount > 0 && !(buf & SPIBUF_TXFULL_MASK)) { + spidat1 = ioread32(dspi->base + SPIDAT1); + dspi->wcount--; + spidat1 &= ~0xFFFF; + spidat1 |= 0xFFFF & dspi->get_tx(dspi); + iowrite32(spidat1, dspi->base + SPIDAT1); } out: @@ -502,21 +498,20 @@ out: static void davinci_spi_dma_callback(unsigned lch, u16 status, void *data) { - struct davinci_spi *davinci_spi = data; - struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + struct davinci_spi *dspi = data; + struct davinci_spi_dma *dma = &dspi->dma; edma_stop(lch); if (status == DMA_COMPLETE) { - if (lch == davinci_spi_dma->dma_rx_channel) - davinci_spi->rcount = 0; - if (lch == davinci_spi_dma->dma_tx_channel) - davinci_spi->wcount = 0; + if (lch == dma->rx_channel) + dspi->rcount = 0; + if (lch == dma->tx_channel) + dspi->wcount = 0; } - if ((!davinci_spi->wcount && !davinci_spi->rcount) || - (status != DMA_COMPLETE)) - complete(&davinci_spi->done); + if ((!dspi->wcount && !dspi->rcount) || (status != DMA_COMPLETE)) + complete(&dspi->done); } /** @@ -530,57 +525,57 @@ static void davinci_spi_dma_callback(unsigned lch, u16 status, void *data) */ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) { - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; int data_type, ret; - u32 tx_data, data1_reg_val; + u32 tx_data, spidat1; u32 errors = 0; struct davinci_spi_config *spicfg; struct davinci_spi_platform_data *pdata; unsigned uninitialized_var(rx_buf_count); struct device *sdev; - davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; + dspi = spi_master_get_devdata(spi->master); + pdata = dspi->pdata; spicfg = (struct davinci_spi_config *)spi->controller_data; if (!spicfg) spicfg = &davinci_spi_default_cfg; - sdev = davinci_spi->bitbang.master->dev.parent; + sdev = dspi->bitbang.master->dev.parent; /* convert len to words based on bits_per_word */ - data_type = davinci_spi->bytes_per_word[spi->chip_select]; + data_type = dspi->bytes_per_word[spi->chip_select]; - davinci_spi->tx = t->tx_buf; - davinci_spi->rx = t->rx_buf; - davinci_spi->wcount = t->len / data_type; - davinci_spi->rcount = davinci_spi->wcount; + dspi->tx = t->tx_buf; + dspi->rx = t->rx_buf; + dspi->wcount = t->len / data_type; + dspi->rcount = dspi->wcount; - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + spidat1 = ioread32(dspi->base + SPIDAT1); - clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); + clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - INIT_COMPLETION(davinci_spi->done); + INIT_COMPLETION(dspi->done); if (spicfg->io_type == SPI_IO_TYPE_INTR) - set_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKINT); + set_io_bits(dspi->base + SPIINT, SPIINT_MASKINT); if (spicfg->io_type != SPI_IO_TYPE_DMA) { /* start the transfer */ - davinci_spi->wcount--; - tx_data = davinci_spi->get_tx(davinci_spi); - data1_reg_val &= 0xFFFF0000; - data1_reg_val |= tx_data & 0xFFFF; - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + dspi->wcount--; + tx_data = dspi->get_tx(dspi); + spidat1 &= 0xFFFF0000; + spidat1 |= tx_data & 0xFFFF; + iowrite32(spidat1, dspi->base + SPIDAT1); } else { - struct davinci_spi_dma *davinci_spi_dma; + struct davinci_spi_dma *dma; unsigned long tx_reg, rx_reg; struct edmacc_param param; void *rx_buf; - davinci_spi_dma = &davinci_spi->dma_channels; + dma = &dspi->dma; - tx_reg = (unsigned long)davinci_spi->pbase + SPIDAT1; - rx_reg = (unsigned long)davinci_spi->pbase + SPIBUF; + tx_reg = (unsigned long)dspi->pbase + SPIDAT1; + rx_reg = (unsigned long)dspi->pbase + SPIBUF; /* * Transmit DMA setup @@ -596,26 +591,24 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) if (t->tx_buf) { t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, - davinci_spi->wcount, DMA_TO_DEVICE); + dspi->wcount, DMA_TO_DEVICE); if (dma_mapping_error(&spi->dev, t->tx_dma)) { dev_dbg(sdev, "Unable to DMA map %d bytes" - "TX buffer\n", - davinci_spi->wcount); + "TX buffer\n", dspi->wcount); return -ENOMEM; } } - param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_tx_channel); + param.opt = TCINTEN | EDMA_TCC(dma->tx_channel); param.src = t->tx_buf ? t->tx_dma : tx_reg; - param.a_b_cnt = davinci_spi->wcount << 16 | data_type; + param.a_b_cnt = dspi->wcount << 16 | data_type; param.dst = tx_reg; param.src_dst_bidx = t->tx_buf ? data_type : 0; param.link_bcntrld = 0xffff; param.src_dst_cidx = 0; param.ccnt = 1; - edma_write_slot(davinci_spi_dma->dma_tx_channel, ¶m); - edma_link(davinci_spi_dma->dma_tx_channel, - davinci_spi_dma->dummy_param_slot); + edma_write_slot(dma->tx_channel, ¶m); + edma_link(dma->tx_channel, dma->dummy_param_slot); /* * Receive DMA setup @@ -631,10 +624,10 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) if (t->rx_buf) { rx_buf = t->rx_buf; - rx_buf_count = davinci_spi->rcount; + rx_buf_count = dspi->rcount; } else { - rx_buf = davinci_spi->rx_tmp_buf; - rx_buf_count = sizeof(davinci_spi->rx_tmp_buf); + rx_buf = dspi->rx_tmp_buf; + rx_buf_count = sizeof(dspi->rx_tmp_buf); } t->rx_dma = dma_map_single(&spi->dev, rx_buf, rx_buf_count, @@ -643,71 +636,69 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", rx_buf_count); if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, - davinci_spi->wcount, - DMA_TO_DEVICE); + dma_unmap_single(NULL, t->tx_dma, dspi->wcount, + DMA_TO_DEVICE); return -ENOMEM; } - param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_rx_channel); + param.opt = TCINTEN | EDMA_TCC(dma->rx_channel); param.src = rx_reg; - param.a_b_cnt = davinci_spi->rcount << 16 | data_type; + param.a_b_cnt = dspi->rcount << 16 | data_type; param.dst = t->rx_dma; param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; param.link_bcntrld = 0xffff; param.src_dst_cidx = 0; param.ccnt = 1; - edma_write_slot(davinci_spi_dma->dma_rx_channel, ¶m); + edma_write_slot(dma->rx_channel, ¶m); if (pdata->cshold_bug) - iowrite16(data1_reg_val >> 16, - davinci_spi->base + SPIDAT1 + 2); + iowrite16(spidat1 >> 16, dspi->base + SPIDAT1 + 2); - edma_start(davinci_spi_dma->dma_rx_channel); - edma_start(davinci_spi_dma->dma_tx_channel); - set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); + edma_start(dma->rx_channel); + edma_start(dma->tx_channel); + set_io_bits(dspi->base + SPIINT, SPIINT_DMA_REQ_EN); } /* Wait for the transfer to complete */ if (spicfg->io_type != SPI_IO_TYPE_POLL) { - wait_for_completion_interruptible(&(davinci_spi->done)); + wait_for_completion_interruptible(&(dspi->done)); } else { - while (davinci_spi->rcount > 0 || davinci_spi->wcount > 0) { - errors = davinci_spi_process_events(davinci_spi); + while (dspi->rcount > 0 || dspi->wcount > 0) { + errors = davinci_spi_process_events(dspi); if (errors) break; cpu_relax(); } } - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); + clear_io_bits(dspi->base + SPIINT, SPIINT_MASKALL); if (spicfg->io_type == SPI_IO_TYPE_DMA) { if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, + dma_unmap_single(NULL, t->tx_dma, dspi->wcount, DMA_TO_DEVICE); dma_unmap_single(NULL, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); + clear_io_bits(dspi->base + SPIINT, SPIINT_DMA_REQ_EN); } - clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); + clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); /* * Check for bit error, desync error,parity error,timeout error and * receive overflow errors */ if (errors) { - ret = davinci_spi_check_error(davinci_spi, errors); + ret = davinci_spi_check_error(dspi, errors); WARN(!ret, "%s: error reported but no error found!\n", dev_name(&spi->dev)); return ret; } - if (davinci_spi->rcount != 0 || davinci_spi->wcount != 0) { + if (dspi->rcount != 0 || dspi->wcount != 0) { dev_err(sdev, "SPI data transfer error\n"); return -EIO; } @@ -726,60 +717,56 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) * If transfer length is zero then it will indicate the COMPLETION so that * davinci_spi_bufs function can go ahead. */ -static irqreturn_t davinci_spi_irq(s32 irq, void *context_data) +static irqreturn_t davinci_spi_irq(s32 irq, void *data) { - struct davinci_spi *davinci_spi = context_data; + struct davinci_spi *dspi = data; int status; - status = davinci_spi_process_events(davinci_spi); + status = davinci_spi_process_events(dspi); if (unlikely(status != 0)) - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKINT); + clear_io_bits(dspi->base + SPIINT, SPIINT_MASKINT); - if ((!davinci_spi->rcount && !davinci_spi->wcount) || status) - complete(&davinci_spi->done); + if ((!dspi->rcount && !dspi->wcount) || status) + complete(&dspi->done); return IRQ_HANDLED; } -static int davinci_spi_request_dma(struct davinci_spi *davinci_spi) +static int davinci_spi_request_dma(struct davinci_spi *dspi) { int r; - struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + struct davinci_spi_dma *dma = &dspi->dma; - r = edma_alloc_channel(davinci_spi_dma->dma_rx_channel, - davinci_spi_dma_callback, davinci_spi, - davinci_spi_dma->eventq); + r = edma_alloc_channel(dma->rx_channel, davinci_spi_dma_callback, dspi, + dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI RX\n"); r = -EAGAIN; goto rx_dma_failed; } - r = edma_alloc_channel(davinci_spi_dma->dma_tx_channel, - davinci_spi_dma_callback, davinci_spi, - davinci_spi_dma->eventq); + r = edma_alloc_channel(dma->tx_channel, davinci_spi_dma_callback, dspi, + dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI TX\n"); r = -EAGAIN; goto tx_dma_failed; } - r = edma_alloc_slot(EDMA_CTLR(davinci_spi_dma->dma_tx_channel), - EDMA_SLOT_ANY); + r = edma_alloc_slot(EDMA_CTLR(dma->tx_channel), EDMA_SLOT_ANY); if (r < 0) { pr_err("Unable to request SPI TX DMA param slot\n"); r = -EAGAIN; goto param_failed; } - davinci_spi_dma->dummy_param_slot = r; - edma_link(davinci_spi_dma->dummy_param_slot, - davinci_spi_dma->dummy_param_slot); + dma->dummy_param_slot = r; + edma_link(dma->dummy_param_slot, dma->dummy_param_slot); return 0; param_failed: - edma_free_channel(davinci_spi_dma->dma_tx_channel); + edma_free_channel(dma->tx_channel); tx_dma_failed: - edma_free_channel(davinci_spi_dma->dma_rx_channel); + edma_free_channel(dma->rx_channel); rx_dma_failed: return r; } @@ -798,7 +785,7 @@ rx_dma_failed: static int davinci_spi_probe(struct platform_device *pdev) { struct spi_master *master; - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; struct davinci_spi_platform_data *pdata; struct resource *r, *mem; resource_size_t dma_rx_chan = SPI_NO_RESOURCE; @@ -821,8 +808,8 @@ static int davinci_spi_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, master); - davinci_spi = spi_master_get_devdata(master); - if (davinci_spi == NULL) { + dspi = spi_master_get_devdata(master); + if (dspi == NULL) { ret = -ENOENT; goto free_master; } @@ -833,8 +820,8 @@ static int davinci_spi_probe(struct platform_device *pdev) goto free_master; } - davinci_spi->pbase = r->start; - davinci_spi->pdata = pdata; + dspi->pbase = r->start; + dspi->pdata = pdata; mem = request_mem_region(r->start, resource_size(r), pdev->name); if (mem == NULL) { @@ -842,48 +829,48 @@ static int davinci_spi_probe(struct platform_device *pdev) goto free_master; } - davinci_spi->base = ioremap(r->start, resource_size(r)); - if (davinci_spi->base == NULL) { + dspi->base = ioremap(r->start, resource_size(r)); + if (dspi->base == NULL) { ret = -ENOMEM; goto release_region; } - davinci_spi->irq = platform_get_irq(pdev, 0); - if (davinci_spi->irq <= 0) { + dspi->irq = platform_get_irq(pdev, 0); + if (dspi->irq <= 0) { ret = -EINVAL; goto unmap_io; } - ret = request_irq(davinci_spi->irq, davinci_spi_irq, 0, - dev_name(&pdev->dev), davinci_spi); + ret = request_irq(dspi->irq, davinci_spi_irq, 0, dev_name(&pdev->dev), + dspi); if (ret) goto unmap_io; - davinci_spi->bitbang.master = spi_master_get(master); - if (davinci_spi->bitbang.master == NULL) { + dspi->bitbang.master = spi_master_get(master); + if (dspi->bitbang.master == NULL) { ret = -ENODEV; goto irq_free; } - davinci_spi->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(davinci_spi->clk)) { + dspi->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dspi->clk)) { ret = -ENODEV; goto put_master; } - clk_enable(davinci_spi->clk); + clk_enable(dspi->clk); master->bus_num = pdev->id; master->num_chipselect = pdata->num_chipselect; master->setup = davinci_spi_setup; - davinci_spi->bitbang.chipselect = davinci_spi_chipselect; - davinci_spi->bitbang.setup_transfer = davinci_spi_setup_transfer; + dspi->bitbang.chipselect = davinci_spi_chipselect; + dspi->bitbang.setup_transfer = davinci_spi_setup_transfer; - davinci_spi->version = pdata->version; + dspi->version = pdata->version; - davinci_spi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP; - if (davinci_spi->version == SPI_VERSION_2) - davinci_spi->bitbang.flags |= SPI_READY; + dspi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP; + if (dspi->version == SPI_VERSION_2) + dspi->bitbang.flags |= SPI_READY; r = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (r) @@ -895,15 +882,15 @@ static int davinci_spi_probe(struct platform_device *pdev) if (r) dma_eventq = r->start; - davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs; + dspi->bitbang.txrx_bufs = davinci_spi_bufs; if (dma_rx_chan != SPI_NO_RESOURCE && dma_tx_chan != SPI_NO_RESOURCE && dma_eventq != SPI_NO_RESOURCE) { - davinci_spi->dma_channels.dma_rx_channel = dma_rx_chan; - davinci_spi->dma_channels.dma_tx_channel = dma_tx_chan; - davinci_spi->dma_channels.eventq = dma_eventq; + dspi->dma.rx_channel = dma_rx_chan; + dspi->dma.tx_channel = dma_tx_chan; + dspi->dma.eventq = dma_eventq; - ret = davinci_spi_request_dma(davinci_spi); + ret = davinci_spi_request_dma(dspi); if (ret) goto free_clk; @@ -913,19 +900,19 @@ static int davinci_spi_probe(struct platform_device *pdev) dma_eventq); } - davinci_spi->get_rx = davinci_spi_rx_buf_u8; - davinci_spi->get_tx = davinci_spi_tx_buf_u8; + dspi->get_rx = davinci_spi_rx_buf_u8; + dspi->get_tx = davinci_spi_tx_buf_u8; - init_completion(&davinci_spi->done); + init_completion(&dspi->done); /* Reset In/OUT SPI module */ - iowrite32(0, davinci_spi->base + SPIGCR0); + iowrite32(0, dspi->base + SPIGCR0); udelay(100); - iowrite32(1, davinci_spi->base + SPIGCR0); + iowrite32(1, dspi->base + SPIGCR0); /* Set up SPIPC0. CS and ENA init is done in davinci_spi_setup */ spipc0 = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK; - iowrite32(spipc0, davinci_spi->base + SPIPC0); + iowrite32(spipc0, dspi->base + SPIPC0); /* initialize chip selects */ if (pdata->chip_sel) { @@ -936,40 +923,40 @@ static int davinci_spi_probe(struct platform_device *pdev) } if (pdata->intr_line) - iowrite32(SPI_INTLVL_1, davinci_spi->base + SPILVL); + iowrite32(SPI_INTLVL_1, dspi->base + SPILVL); else - iowrite32(SPI_INTLVL_0, davinci_spi->base + SPILVL); + iowrite32(SPI_INTLVL_0, dspi->base + SPILVL); - iowrite32(CS_DEFAULT, davinci_spi->base + SPIDEF); + iowrite32(CS_DEFAULT, dspi->base + SPIDEF); /* master mode default */ - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_MASTER_MASK); - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_MASTER_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); - ret = spi_bitbang_start(&davinci_spi->bitbang); + ret = spi_bitbang_start(&dspi->bitbang); if (ret) goto free_dma; - dev_info(&pdev->dev, "Controller at 0x%p\n", davinci_spi->base); + dev_info(&pdev->dev, "Controller at 0x%p\n", dspi->base); return ret; free_dma: - edma_free_channel(davinci_spi->dma_channels.dma_tx_channel); - edma_free_channel(davinci_spi->dma_channels.dma_rx_channel); - edma_free_slot(davinci_spi->dma_channels.dummy_param_slot); + edma_free_channel(dspi->dma.tx_channel); + edma_free_channel(dspi->dma.rx_channel); + edma_free_slot(dspi->dma.dummy_param_slot); free_clk: - clk_disable(davinci_spi->clk); - clk_put(davinci_spi->clk); + clk_disable(dspi->clk); + clk_put(dspi->clk); put_master: spi_master_put(master); irq_free: - free_irq(davinci_spi->irq, davinci_spi); + free_irq(dspi->irq, dspi); unmap_io: - iounmap(davinci_spi->base); + iounmap(dspi->base); release_region: - release_mem_region(davinci_spi->pbase, resource_size(r)); + release_mem_region(dspi->pbase, resource_size(r)); free_master: kfree(master); err: @@ -987,22 +974,22 @@ err: */ static int __exit davinci_spi_remove(struct platform_device *pdev) { - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; struct spi_master *master; struct resource *r; master = dev_get_drvdata(&pdev->dev); - davinci_spi = spi_master_get_devdata(master); + dspi = spi_master_get_devdata(master); - spi_bitbang_stop(&davinci_spi->bitbang); + spi_bitbang_stop(&dspi->bitbang); - clk_disable(davinci_spi->clk); - clk_put(davinci_spi->clk); + clk_disable(dspi->clk); + clk_put(dspi->clk); spi_master_put(master); - free_irq(davinci_spi->irq, davinci_spi); - iounmap(davinci_spi->base); + free_irq(dspi->irq, dspi); + iounmap(dspi->base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(davinci_spi->pbase, resource_size(r)); + release_mem_region(dspi->pbase, resource_size(r)); return 0; } -- cgit v1.2.2 From 23ce17adb7fc33a4353abe4b57a03f555cced57b Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Tue, 12 Oct 2010 11:58:02 +0530 Subject: spi: davinci: kconfig: add manufacturer name to prompt string Add manufacturer name to the Kconfig prompt string and move the controller name to the begining of the prompt. This helps locate the driver easily among the list of existing drivers. While at it, also add information about being able to build the driver as module. Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/Kconfig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 78f9fd02c1b2..a32796e0bdc9 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -111,11 +111,14 @@ config SPI_COLDFIRE_QSPI will be called coldfire_qspi. config SPI_DAVINCI - tristate "SPI controller driver for DaVinci/DA8xx SoC's" + tristate "Texas Instruments DaVinci/DA8x/OMAP-L/AM1x SoC SPI controller" depends on SPI_MASTER && ARCH_DAVINCI select SPI_BITBANG help - SPI master controller for DaVinci and DA8xx SPI modules. + SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules. + + This driver can also be built as a module. The module will be called + davinci_spi. config SPI_EP93XX tristate "Cirrus Logic EP93xx SPI controller" -- cgit v1.2.2 From fbd29a14af42d374ddce9c16ff5f7805e69c764f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 19 Nov 2010 09:00:11 -0800 Subject: spi/pxa2xx: register driver properly use platform_driver_register instead of platform_driver_probe. The latter only checks available devices at the time of calling. So if a device gets inserter at a later point in time then the driver will never play with it. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- drivers/spi/pxa2xx_spi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index e76b1afafe07..4e169b579efb 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1366,7 +1366,7 @@ static void cleanup(struct spi_device *spi) kfree(chip); } -static int __init init_queue(struct driver_data *drv_data) +static int __devinit init_queue(struct driver_data *drv_data) { INIT_LIST_HEAD(&drv_data->queue); spin_lock_init(&drv_data->lock); @@ -1454,7 +1454,7 @@ static int destroy_queue(struct driver_data *drv_data) return 0; } -static int __init pxa2xx_spi_probe(struct platform_device *pdev) +static int __devinit pxa2xx_spi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct pxa2xx_spi_master *platform_info; @@ -1723,13 +1723,14 @@ static struct platform_driver driver = { .pm = &pxa2xx_spi_pm_ops, #endif }, + .probe = pxa2xx_spi_probe, .remove = pxa2xx_spi_remove, .shutdown = pxa2xx_spi_shutdown, }; static int __init pxa2xx_spi_init(void) { - return platform_driver_probe(&driver, pxa2xx_spi_probe); + return platform_driver_register(&driver); } subsys_initcall(pxa2xx_spi_init); -- cgit v1.2.2 From 214b574ab81236e7740243985d63a1d6a61231a2 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 20 Nov 2010 14:52:53 +0800 Subject: atmel_spi: fix warning In function 'atmel_spi_dma_map_xfer' passing argument 2 of 'dma_map_single' discards qualifiers from pointer target type Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Nicolas Ferre --- drivers/spi/atmel_spi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 154529aacc03..a067046c9da2 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -352,8 +352,12 @@ atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer) xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS; if (xfer->tx_buf) { + /* tx_buf is a const void* where we need a void * for the dma + * mapping */ + void *nonconst_tx = (void *)xfer->tx_buf; + xfer->tx_dma = dma_map_single(dev, - (void *) xfer->tx_buf, xfer->len, + nonconst_tx, xfer->len, DMA_TO_DEVICE); if (dma_mapping_error(dev, xfer->tx_dma)) return -ENOMEM; -- cgit v1.2.2 From 49cbb1e0b6dcba9170e72fdf40c75fc24cadba4b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Nov 2010 17:12:14 -0800 Subject: spi/pxa2xx: add support for shared IRQ handler This is required in case the interrupt line is shared with other devices. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- drivers/spi/pxa2xx_spi.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 4e169b579efb..1865c23f2175 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -742,6 +742,18 @@ static irqreturn_t ssp_int(int irq, void *dev_id) { struct driver_data *drv_data = dev_id; void __iomem *reg = drv_data->ioaddr; + u32 sccr1_reg = read_SSCR1(reg); + u32 mask = drv_data->mask_sr; + u32 status; + + status = read_SSSR(reg); + + /* Ignore possible writes if we don't need to write */ + if (!(sccr1_reg & SSCR1_TIE)) + mask &= ~SSSR_TFS; + + if (!(status & mask)) + return IRQ_NONE; if (!drv_data->cur_msg) { @@ -1512,7 +1524,8 @@ static int __devinit pxa2xx_spi_probe(struct platform_device *pdev) drv_data->mask_sr = SSSR_TINT | SSSR_RFS | SSSR_TFS | SSSR_ROR; } - status = request_irq(ssp->irq, ssp_int, 0, dev_name(dev), drv_data); + status = request_irq(ssp->irq, ssp_int, IRQF_SHARED, dev_name(dev), + drv_data); if (status < 0) { dev_err(&pdev->dev, "cannot get IRQ %d\n", ssp->irq); goto out_error_master_alloc; -- cgit v1.2.2 From 4a25605fb71f02b4f80091df91f777225a0309c5 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Nov 2010 17:12:15 -0800 Subject: spi/pxa2xx: Use define for SSSR_TFL_MASK instead of plain numbers Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- drivers/spi/pxa2xx_spi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 1865c23f2175..55083445aae6 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -224,7 +224,7 @@ static int null_writer(struct driver_data *drv_data) void __iomem *reg = drv_data->ioaddr; u8 n_bytes = drv_data->n_bytes; - if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) + if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK) || (drv_data->tx == drv_data->tx_end)) return 0; @@ -252,7 +252,7 @@ static int u8_writer(struct driver_data *drv_data) { void __iomem *reg = drv_data->ioaddr; - if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) + if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK) || (drv_data->tx == drv_data->tx_end)) return 0; @@ -279,7 +279,7 @@ static int u16_writer(struct driver_data *drv_data) { void __iomem *reg = drv_data->ioaddr; - if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) + if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK) || (drv_data->tx == drv_data->tx_end)) return 0; @@ -306,7 +306,7 @@ static int u32_writer(struct driver_data *drv_data) { void __iomem *reg = drv_data->ioaddr; - if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) + if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK) || (drv_data->tx == drv_data->tx_end)) return 0; -- cgit v1.2.2 From 8348c259dd6a6019a8fa01b0a3443409480f7b9d Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Nov 2010 17:12:15 -0800 Subject: arm/pxa2xx: reorgazine SSP and SPI header files The PXA-SPI driver relies on some files / defines which are arm specific and are within the ARM tree. The CE4100 SoC which is x86 has also the SPI core. This patch moves the ssp and spi files from arm/mach-pxa and plat-pxa to include/linux where the CE4100 can access them. This move got verified by building the following defconfigs: cm_x2xx_defconfig corgi_defconfig em_x270_defconfig ezx_defconfig imote2_defconfig pxa3xx_defconfig spitz_defconfig zeus_defconfig raumfeld_defconfig magician_defconfig Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- drivers/spi/pxa2xx_spi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 55083445aae6..98d9c8b09182 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -35,9 +36,6 @@ #include #include -#include -#include -#include MODULE_AUTHOR("Stephen Street"); MODULE_DESCRIPTION("PXA2xx SSP SPI Controller"); -- cgit v1.2.2 From d6ea3df0d470fb9260db93883f97764cf9f0e562 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 24 Nov 2010 10:17:14 +0100 Subject: spi/pxa2xx: Add CE4100 support Sodaville's SPI controller is very much the same as in PXA25x. The difference: - The RX/TX FIFO is only 4 words deep instead of 16 - No DMA support - The SPI controller offers a CS functionality Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- drivers/spi/Kconfig | 13 +-- drivers/spi/Makefile | 1 + drivers/spi/pxa2xx_spi.c | 1 - drivers/spi/pxa2xx_spi_pci.c | 201 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 drivers/spi/pxa2xx_spi_pci.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 78f9fd02c1b2..537759011d1d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -267,12 +267,15 @@ config SPI_PPC4xx config SPI_PXA2XX tristate "PXA2xx SSP SPI master" - depends on ARCH_PXA && EXPERIMENTAL - select PXA_SSP + depends on (ARCH_PXA || (X86_32 && PCI)) && EXPERIMENTAL + select PXA_SSP if ARCH_PXA help - This enables using a PXA2xx SSP port as a SPI master controller. - The driver can be configured to use any SSP port and additional - documentation can be found a Documentation/spi/pxa2xx. + This enables using a PXA2xx or Sodaville SSP port as a SPI master + controller. The driver can be configured to use any SSP port and + additional documentation can be found a Documentation/spi/pxa2xx. + +config SPI_PXA2XX_PCI + def_bool SPI_PXA2XX && X86_32 && PCI config SPI_S3C24XX tristate "Samsung S3C24XX series SPI" diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 8bc1a5abac1f..bdc4c400fd71 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_SPI_GPIO) += spi_gpio.o obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o +obj-$(CONFIG_SPI_PXA2XX_PCI) += pxa2xx_spi_pci.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 98d9c8b09182..ed212c2583ab 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/drivers/spi/pxa2xx_spi_pci.c b/drivers/spi/pxa2xx_spi_pci.c new file mode 100644 index 000000000000..351d8a375b57 --- /dev/null +++ b/drivers/spi/pxa2xx_spi_pci.c @@ -0,0 +1,201 @@ +/* + * CE4100's SPI device is more or less the same one as found on PXA + * + */ +#include +#include +#include +#include + +struct awesome_struct { + struct ssp_device ssp; + struct platform_device spi_pdev; + struct pxa2xx_spi_master spi_pdata; +}; + +static DEFINE_MUTEX(ssp_lock); +static LIST_HEAD(ssp_list); + +struct ssp_device *pxa_ssp_request(int port, const char *label) +{ + struct ssp_device *ssp = NULL; + + mutex_lock(&ssp_lock); + + list_for_each_entry(ssp, &ssp_list, node) { + if (ssp->port_id == port && ssp->use_count == 0) { + ssp->use_count++; + ssp->label = label; + break; + } + } + + mutex_unlock(&ssp_lock); + + if (&ssp->node == &ssp_list) + return NULL; + + return ssp; +} +EXPORT_SYMBOL_GPL(pxa_ssp_request); + +void pxa_ssp_free(struct ssp_device *ssp) +{ + mutex_lock(&ssp_lock); + if (ssp->use_count) { + ssp->use_count--; + ssp->label = NULL; + } else + dev_err(&ssp->pdev->dev, "device already free\n"); + mutex_unlock(&ssp_lock); +} +EXPORT_SYMBOL_GPL(pxa_ssp_free); + +static void plat_dev_release(struct device *dev) +{ + struct awesome_struct *as = container_of(dev, + struct awesome_struct, spi_pdev.dev); + + of_device_node_put(&as->spi_pdev.dev); +} + +static int __devinit ce4100_spi_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + int ret; + resource_size_t phys_beg; + resource_size_t phys_len; + struct awesome_struct *spi_info; + struct platform_device *pdev; + struct pxa2xx_spi_master *spi_pdata; + struct ssp_device *ssp; + + ret = pci_enable_device(dev); + if (ret) + return ret; + + phys_beg = pci_resource_start(dev, 0); + phys_len = pci_resource_len(dev, 0); + + if (!request_mem_region(phys_beg, phys_len, + "CE4100 SPI")) { + dev_err(&dev->dev, "Can't request register space.\n"); + ret = -EBUSY; + return ret; + } + + spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL); + if (!spi_info) { + ret = -ENOMEM; + goto err_kz; + } + ssp = &spi_info->ssp; + pdev = &spi_info->spi_pdev; + spi_pdata = &spi_info->spi_pdata; + + pdev->name = "pxa2xx-spi"; + pdev->id = dev->devfn; + pdev->dev.parent = &dev->dev; + pdev->dev.platform_data = &spi_info->spi_pdata; + +#ifdef CONFIG_OF + pdev->dev.of_node = dev->dev.of_node; +#endif + pdev->dev.release = plat_dev_release; + + spi_pdata->num_chipselect = dev->devfn; + + ssp->phys_base = pci_resource_start(dev, 0); + ssp->mmio_base = ioremap(phys_beg, phys_len); + if (!ssp->mmio_base) { + dev_err(&pdev->dev, "failed to ioremap() registers\n"); + ret = -EIO; + goto err_remap; + } + ssp->irq = dev->irq; + ssp->port_id = pdev->id; + ssp->type = PXA25x_SSP; + + mutex_lock(&ssp_lock); + list_add(&ssp->node, &ssp_list); + mutex_unlock(&ssp_lock); + + pci_set_drvdata(dev, spi_info); + + ret = platform_device_register(pdev); + if (ret) + goto err_dev_add; + + return ret; + +err_dev_add: + pci_set_drvdata(dev, NULL); + mutex_lock(&ssp_lock); + list_del(&ssp->node); + mutex_unlock(&ssp_lock); + iounmap(ssp->mmio_base); + +err_remap: + kfree(spi_info); + +err_kz: + release_mem_region(phys_beg, phys_len); + + return ret; +} + +static void __devexit ce4100_spi_remove(struct pci_dev *dev) +{ + struct awesome_struct *spi_info; + struct platform_device *pdev; + struct ssp_device *ssp; + + spi_info = pci_get_drvdata(dev); + + ssp = &spi_info->ssp; + pdev = &spi_info->spi_pdev; + + platform_device_unregister(pdev); + + iounmap(ssp->mmio_base); + release_mem_region(pci_resource_start(dev, 0), + pci_resource_len(dev, 0)); + + mutex_lock(&ssp_lock); + list_del(&ssp->node); + mutex_unlock(&ssp_lock); + + pci_set_drvdata(dev, NULL); + pci_disable_device(dev); + kfree(spi_info); +} + +static struct pci_device_id ce4100_spi_devices[] __devinitdata = { + + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) }, + { }, +}; +MODULE_DEVICE_TABLE(pci, ce4100_spi_devices); + +static struct pci_driver ce4100_spi_driver = { + .name = "ce4100_spi", + .id_table = ce4100_spi_devices, + .probe = ce4100_spi_probe, + .remove = __devexit_p(ce4100_spi_remove), +}; + +static int __init ce4100_spi_init(void) +{ + return pci_register_driver(&ce4100_spi_driver); +} +module_init(ce4100_spi_init); + +static void __exit ce4100_spi_exit(void) +{ + pci_unregister_driver(&ce4100_spi_driver); +} +module_exit(ce4100_spi_exit); + +MODULE_DESCRIPTION("CE4100 PCI-SPI glue code for PXA's driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sebastian Andrzej Siewior "); -- cgit v1.2.2 From d0777f2c3eda180e3fc549e0efbe741014f17689 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Nov 2010 17:12:16 -0800 Subject: spi/pxa2xx: Consider CE4100's FIFO depth For PXA the default threshold is FIFO_DEPTH / 2. Adjust this value for CE4100. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- drivers/spi/pxa2xx_spi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index ed212c2583ab..81cfbbc58e94 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -43,8 +43,6 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define MAX_BUSES 3 -#define RX_THRESH_DFLT 8 -#define TX_THRESH_DFLT 8 #define TIMOUT_DFLT 1000 #define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) -- cgit v1.2.2 From 2a8626a9e2d86d114a2d9f813a1acebf9d53dd10 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Nov 2010 17:12:17 -0800 Subject: spi/pxa2xx: Add chipselect support for Sodaville The SPI core on Sodaville supports chip selects. Its configuration moved into the SSSR register at bit 0 and 1. Thus Sodaville can be hooked up with up to 4 devices. This patch ensures that the bits which are otherwiese reserved are only touched on Sodaville and not on any other PXAs. Also it makes sure that the status register does not lose the CS information while clearing the ROR bit. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- drivers/spi/pxa2xx_spi.c | 93 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 25 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 81cfbbc58e94..a54685bb7e53 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -163,7 +163,10 @@ struct chip_data { u8 enable_dma; u8 bits_per_word; u32 speed_hz; - int gpio_cs; + union { + int gpio_cs; + unsigned int frm; + }; int gpio_cs_inverted; int (*write)(struct driver_data *drv_data); int (*read)(struct driver_data *drv_data); @@ -176,6 +179,11 @@ static void cs_assert(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; + if (drv_data->ssp_type == CE4100_SSP) { + write_SSSR(drv_data->cur_chip->frm, drv_data->ioaddr); + return; + } + if (chip->cs_control) { chip->cs_control(PXA2XX_CS_ASSERT); return; @@ -189,6 +197,9 @@ static void cs_deassert(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; + if (drv_data->ssp_type == CE4100_SSP) + return; + if (chip->cs_control) { chip->cs_control(PXA2XX_CS_DEASSERT); return; @@ -198,6 +209,25 @@ static void cs_deassert(struct driver_data *drv_data) gpio_set_value(chip->gpio_cs, !chip->gpio_cs_inverted); } +static void write_SSSR_CS(struct driver_data *drv_data, u32 val) +{ + void __iomem *reg = drv_data->ioaddr; + + if (drv_data->ssp_type == CE4100_SSP) + val |= read_SSSR(reg) & SSSR_ALT_FRM_MASK; + + write_SSSR(val, reg); +} + +static int pxa25x_ssp_comp(struct driver_data *drv_data) +{ + if (drv_data->ssp_type == PXA25x_SSP) + return 1; + if (drv_data->ssp_type == CE4100_SSP) + return 1; + return 0; +} + static int flush(struct driver_data *drv_data) { unsigned long limit = loops_per_jiffy << 1; @@ -209,7 +239,7 @@ static int flush(struct driver_data *drv_data) read_SSDR(reg); } } while ((read_SSSR(reg) & SSSR_BSY) && --limit); - write_SSSR(SSSR_ROR, reg); + write_SSSR_CS(drv_data, SSSR_ROR); return limit; } @@ -502,9 +532,9 @@ static void dma_error_stop(struct driver_data *drv_data, const char *msg) /* Stop and reset */ DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; - write_SSSR(drv_data->clear_sr, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); flush(drv_data); write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); @@ -524,7 +554,7 @@ static void dma_transfer_complete(struct driver_data *drv_data) /* Clear and disable interrupts on SSP and DMA channels*/ write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); - write_SSSR(drv_data->clear_sr, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; @@ -617,7 +647,7 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data) /* Clear and disable timeout interrupt, do the rest in * dma_transfer_complete */ - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); /* finish this transfer, start the next */ @@ -635,9 +665,9 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg) void __iomem *reg = drv_data->ioaddr; /* Stop and reset SSP */ - write_SSSR(drv_data->clear_sr, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); flush(drv_data); write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); @@ -653,9 +683,9 @@ static void int_transfer_complete(struct driver_data *drv_data) void __iomem *reg = drv_data->ioaddr; /* Stop SSP */ - write_SSSR(drv_data->clear_sr, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); /* Update total byte transfered return count actual bytes read */ @@ -711,7 +741,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) if (drv_data->tx == drv_data->tx_end) { write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg); /* PXA25x_SSP has no timeout, read trailing bytes */ - if (drv_data->ssp_type == PXA25x_SSP) { + if (pxa25x_ssp_comp(drv_data)) { if (!wait_ssp_rx_stall(reg)) { int_error_stop(drv_data, "interrupt_transfer: " @@ -754,9 +784,9 @@ static irqreturn_t ssp_int(int irq, void *dev_id) write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); - write_SSSR(drv_data->clear_sr, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); dev_err(&drv_data->pdev->dev, "bad message state " "in interrupt handler\n"); @@ -869,7 +899,7 @@ static unsigned int ssp_get_clk_div(struct ssp_device *ssp, int rate) { unsigned long ssp_clk = clk_get_rate(ssp->clk); - if (ssp->type == PXA25x_SSP) + if (ssp->type == PXA25x_SSP || ssp->type == CE4100_SSP) return ((ssp_clk / (2 * rate) - 1) & 0xff) << 8; else return ((ssp_clk / rate - 1) & 0xfff) << 8; @@ -1095,7 +1125,7 @@ static void pump_transfers(unsigned long data) /* Clear status */ cr1 = chip->cr1 | chip->threshold | drv_data->int_cr1; - write_SSSR(drv_data->clear_sr, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); } /* see if we need to reload the config registers */ @@ -1105,7 +1135,7 @@ static void pump_transfers(unsigned long data) /* stop the SSP, and update the other bits */ write_SSCR0(cr0 & ~SSCR0_SSE, reg); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(chip->timeout, reg); /* first set CR1 without interrupt and service enables */ write_SSCR1(cr1 & SSCR1_CHANGE_MASK, reg); @@ -1113,7 +1143,7 @@ static void pump_transfers(unsigned long data) write_SSCR0(cr0, reg); } else { - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(chip->timeout, reg); } @@ -1240,14 +1270,13 @@ static int setup(struct spi_device *spi) uint tx_thres = TX_THRESH_DFLT; uint rx_thres = RX_THRESH_DFLT; - if (drv_data->ssp_type != PXA25x_SSP + if (!pxa25x_ssp_comp(drv_data) && (spi->bits_per_word < 4 || spi->bits_per_word > 32)) { dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d " "b/w not 4-32 for type non-PXA25x_SSP\n", drv_data->ssp_type, spi->bits_per_word); return -EINVAL; - } - else if (drv_data->ssp_type == PXA25x_SSP + } else if (pxa25x_ssp_comp(drv_data) && (spi->bits_per_word < 4 || spi->bits_per_word > 16)) { dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d " @@ -1266,7 +1295,17 @@ static int setup(struct spi_device *spi) return -ENOMEM; } - chip->gpio_cs = -1; + if (drv_data->ssp_type == CE4100_SSP) { + if (spi->chip_select > 4) { + dev_err(&spi->dev, "failed setup: " + "cs number must not be > 4.\n"); + kfree(chip); + return -EINVAL; + } + + chip->frm = spi->chip_select; + } else + chip->gpio_cs = -1; chip->enable_dma = 0; chip->timeout = TIMOUT_DFLT; chip->dma_burst_size = drv_data->master_info->enable_dma ? @@ -1322,7 +1361,7 @@ static int setup(struct spi_device *spi) | (((spi->mode & SPI_CPOL) != 0) ? SSCR1_SPO : 0); /* NOTE: PXA25x_SSP _could_ use external clocking ... */ - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) dev_dbg(&spi->dev, "%ld Hz actual, %s\n", clk_get_rate(ssp->clk) / (1 + ((chip->cr0 & SSCR0_SCR(0xfff)) >> 8)), @@ -1357,17 +1396,21 @@ static int setup(struct spi_device *spi) spi_set_ctldata(spi, chip); + if (drv_data->ssp_type == CE4100_SSP) + return 0; + return setup_cs(spi, chip, chip_info); } static void cleanup(struct spi_device *spi) { struct chip_data *chip = spi_get_ctldata(spi); + struct driver_data *drv_data = spi_master_get_devdata(spi->master); if (!chip) return; - if (gpio_is_valid(chip->gpio_cs)) + if (drv_data->ssp_type != CE4100_SSP && gpio_is_valid(chip->gpio_cs)) gpio_free(chip->gpio_cs); kfree(chip); @@ -1507,7 +1550,7 @@ static int __devinit pxa2xx_spi_probe(struct platform_device *pdev) drv_data->ioaddr = ssp->mmio_base; drv_data->ssdr_physical = ssp->phys_base + SSDR; - if (ssp->type == PXA25x_SSP) { + if (pxa25x_ssp_comp(drv_data)) { drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE; drv_data->dma_cr1 = 0; drv_data->clear_sr = SSSR_ROR; @@ -1569,7 +1612,7 @@ static int __devinit pxa2xx_spi_probe(struct platform_device *pdev) | SSCR0_Motorola | SSCR0_DataSize(8), drv_data->ioaddr); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, drv_data->ioaddr); write_SSPSP(0, drv_data->ioaddr); -- cgit v1.2.2 From 579d3bb2ac1a351bbf536480a9ab38199bbf901d Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Nov 2010 17:12:17 -0800 Subject: spi/pxa2xx: Modify RX-Tresh instead of busy-loop for the remaining RX bytes. After all TX bytes are sent, the driver spins while the SPI core is busy and then it spins for a "short" period of time until RX bytes are available. On Sodavile the busy flag disappears pretty quick and after that it takes approx ~130ms (sometimes less but not much) until there are bytes available in the RX FIFO. This patch removes the busy loop and modifies the RX threshould so we get woken up once the remainings bytes arrived. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- drivers/spi/pxa2xx_spi.c | 56 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 17 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index a54685bb7e53..9ca6454cfadc 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -660,13 +660,25 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data) return IRQ_NONE; } +static void reset_sccr1(struct driver_data *drv_data) +{ + void __iomem *reg = drv_data->ioaddr; + struct chip_data *chip = drv_data->cur_chip; + u32 sccr1_reg; + + sccr1_reg = read_SSCR1(reg) & ~drv_data->int_cr1; + sccr1_reg &= ~SSCR1_RFT; + sccr1_reg |= chip->threshold; + write_SSCR1(sccr1_reg, reg); +} + static void int_error_stop(struct driver_data *drv_data, const char* msg) { void __iomem *reg = drv_data->ioaddr; /* Stop and reset SSP */ write_SSSR_CS(drv_data, drv_data->clear_sr); - write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); + reset_sccr1(drv_data); if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); flush(drv_data); @@ -684,7 +696,7 @@ static void int_transfer_complete(struct driver_data *drv_data) /* Stop SSP */ write_SSSR_CS(drv_data, drv_data->clear_sr); - write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); + reset_sccr1(drv_data); if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); @@ -739,24 +751,34 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) } if (drv_data->tx == drv_data->tx_end) { - write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg); - /* PXA25x_SSP has no timeout, read trailing bytes */ + u32 bytes_left; + u32 sccr1_reg; + + sccr1_reg = read_SSCR1(reg); + sccr1_reg &= ~SSCR1_TIE; + + /* + * PXA25x_SSP has no timeout, set up rx threshould for the + * remaing RX bytes. + */ if (pxa25x_ssp_comp(drv_data)) { - if (!wait_ssp_rx_stall(reg)) - { - int_error_stop(drv_data, "interrupt_transfer: " - "rx stall failed"); - return IRQ_HANDLED; - } - if (!drv_data->read(drv_data)) - { - int_error_stop(drv_data, - "interrupt_transfer: " - "trailing byte read failed"); - return IRQ_HANDLED; + + sccr1_reg &= ~SSCR1_RFT; + + bytes_left = drv_data->rx_end - drv_data->rx; + switch (drv_data->n_bytes) { + case 4: + bytes_left >>= 1; + case 2: + bytes_left >>= 1; } - int_transfer_complete(drv_data); + + if (bytes_left > RX_THRESH_DFLT) + bytes_left = RX_THRESH_DFLT; + + sccr1_reg |= SSCR1_RxTresh(bytes_left); } + write_SSCR1(sccr1_reg, reg); } /* We did something */ -- cgit v1.2.2 From 21486af0f34d03b813b023d7a2b887b329f60486 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 8 Oct 2010 18:11:19 +0200 Subject: spi/pxa2xx: pass of_node to spi device and set a parent device the of_node will auto-publish devices which are added to the device tree. Signed-off-by: Sebastian Andrzej Siewior --- drivers/spi/pxa2xx_spi.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 9ca6454cfadc..95928833855b 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1556,6 +1556,10 @@ static int __devinit pxa2xx_spi_probe(struct platform_device *pdev) drv_data->pdev = pdev; drv_data->ssp = ssp; + master->dev.parent = &pdev->dev; +#ifdef CONFIG_OF + master->dev.of_node = pdev->dev.of_node; +#endif /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; -- cgit v1.2.2 From 3d0b6087f3f2c88caef25e1455ff8db0816d4e11 Mon Sep 17 00:00:00 2001 From: Major Lee Date: Fri, 10 Dec 2010 10:13:49 +0000 Subject: dw_spi: Fix missing final read in some polling situations There is a possibility that the last word of a transaction will be lost if data is not ready. Re-read in poll_transfer() to solve this issue when poll_mode is enabled. Verified on SPI touch screen device. Signed-off-by: Major Lee Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/spi/dw_spi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 90439314cf67..0838c79861e4 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -413,6 +413,11 @@ static void poll_transfer(struct dw_spi *dws) { while (dws->write(dws)) dws->read(dws); + /* + * There is a possibility that the last word of a transaction + * will be lost if data is not ready. Re-read to solve this issue. + */ + dws->read(dws); transfer_complete(dws); } -- cgit v1.2.2 From 97dbf37d89b6d387a5fe79ffe3b72c37ec12db43 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 21 Dec 2010 17:24:31 -0800 Subject: drivers/spi/spi.c: don't release the spi device twice This was fixed by David Lamparter in v2.6.36-rc5 3486008 ("spi: free children in spi_unregister_master, not siblings") and broken again in v2.6.37-rc1~2^2~4 during the merge of 2b9603a0 ("spi: enable spi_board_info to be registered after spi_master"). Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: David Lamparter Cc: Grant Likely Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 709c836607de..b02d0cbce890 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -584,8 +584,7 @@ void spi_unregister_master(struct spi_master *master) list_del(&master->list); mutex_unlock(&board_lock); - dummy = device_for_each_child(master->dev.parent, &master->dev, - __unregister); + dummy = device_for_each_child(&master->dev, NULL, __unregister); device_unregister(&master->dev); } EXPORT_SYMBOL_GPL(spi_unregister_master); -- cgit v1.2.2 From 893421745a052100b981401b7c5c6dc8708fb8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 24 Nov 2010 10:05:46 +0100 Subject: spi/imx: remove autodetection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no machines in-tree that still use the driver name as device name. So save a few bytes and remove it. Signed-off-by: Uwe Kleine-König Acked-by: Jason Wang Signed-off-by: Grant Likely --- drivers/spi/spi_imx.c | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 55a38e2c6c13..9010ef0385a8 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -66,7 +66,6 @@ enum spi_imx_devtype { SPI_IMX_VER_0_5, SPI_IMX_VER_0_7, SPI_IMX_VER_2_3, - SPI_IMX_VER_AUTODETECT, }; struct spi_imx_data; @@ -720,9 +719,6 @@ static void spi_imx_cleanup(struct spi_device *spi) static struct platform_device_id spi_imx_devtype[] = { { - .name = DRIVER_NAME, - .driver_data = SPI_IMX_VER_AUTODETECT, - }, { .name = "imx1-cspi", .driver_data = SPI_IMX_VER_IMX1, }, { @@ -802,30 +798,8 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) init_completion(&spi_imx->xfer_done); - if (pdev->id_entry->driver_data == SPI_IMX_VER_AUTODETECT) { - if (cpu_is_mx25() || cpu_is_mx35()) - spi_imx->devtype_data = - spi_imx_devtype_data[SPI_IMX_VER_0_7]; - else if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35()) - spi_imx->devtype_data = - spi_imx_devtype_data[SPI_IMX_VER_0_4]; - else if (cpu_is_mx27() || cpu_is_mx21()) - spi_imx->devtype_data = - spi_imx_devtype_data[SPI_IMX_VER_0_0]; - else if (cpu_is_mx1()) - spi_imx->devtype_data = - spi_imx_devtype_data[SPI_IMX_VER_IMX1]; - else - BUG(); - } else - spi_imx->devtype_data = - spi_imx_devtype_data[pdev->id_entry->driver_data]; - - if (!spi_imx->devtype_data.intctrl) { - dev_err(&pdev->dev, "no support for this device compiled in\n"); - ret = -ENODEV; - goto out_gpio_free; - } + spi_imx->devtype_data = + spi_imx_devtype_data[pdev->id_entry->driver_data]; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { -- cgit v1.2.2 From 9d32af66aa3a0e28d95fc607e208795ef6cc993a Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 24 Dec 2010 11:40:50 +0900 Subject: spi/topcliff_pch: Fix data transfer issue It seems spi_topcliff_pch of linux-2.6.37-rc6 degraded by previous patch. In fact, data transfer fails on evaluation board testing. I found like the following register miss-setting line. Using this patch, I have confirmed data transfer can work well. Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/spi/spi_topcliff_pch.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c index 58e187f45ec7..56b758847c04 100644 --- a/drivers/spi/spi_topcliff_pch.c +++ b/drivers/spi/spi_topcliff_pch.c @@ -267,7 +267,7 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, if (reg_spsr_val & SPSR_FI_BIT) { /* disable FI & RFI interrupts */ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, - SPCR_FIE_BIT | SPCR_TFIE_BIT); + SPCR_FIE_BIT | SPCR_RFIE_BIT); /* transfer is completed;inform pch_spi_process_messages */ data->transfer_complete = true; @@ -679,11 +679,11 @@ static void pch_spi_set_ir(struct pch_spi_data *data) if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) { /* set receive threhold to PCH_RX_THOLD */ pch_spi_setclr_reg(data->master, PCH_SPCR, - PCH_RX_THOLD << SPCR_TFIC_FIELD, - ~MASK_TFIC_SPCR_BITS); + PCH_RX_THOLD << SPCR_RFIC_FIELD, + ~MASK_RFIC_SPCR_BITS); /* enable FI and RFI interrupts */ pch_spi_setclr_reg(data->master, PCH_SPCR, - SPCR_RFIE_BIT | SPCR_TFIE_BIT, 0); + SPCR_RFIE_BIT | SPCR_FIE_BIT, 0); } else { /* set receive threhold to maximum */ pch_spi_setclr_reg(data->master, PCH_SPCR, -- cgit v1.2.2 From b729889686afb7d4366e07fe9c2f7a2737166462 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Dec 2010 23:13:07 +0100 Subject: spi/pl022: map the buffers on the DMA engine The struct device for the DMA engine is the apropriate one to use when mapping/unmapping buffers. This is because the memory which is addressable by DMA is determined by the DMA engine rather than the device. Reported-by: Russell King Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 2e506319b60f..e29751af5c7b 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -782,9 +782,9 @@ static void *next_transfer(struct pl022 *pl022) static void unmap_free_dma_scatter(struct pl022 *pl022) { /* Unmap and free the SG tables */ - dma_unmap_sg(&pl022->adev->dev, pl022->sgt_tx.sgl, + dma_unmap_sg(pl022->dma_tx_channel->device->dev, pl022->sgt_tx.sgl, pl022->sgt_tx.nents, DMA_TO_DEVICE); - dma_unmap_sg(&pl022->adev->dev, pl022->sgt_rx.sgl, + dma_unmap_sg(pl022->dma_rx_channel->device->dev, pl022->sgt_rx.sgl, pl022->sgt_rx.nents, DMA_FROM_DEVICE); sg_free_table(&pl022->sgt_rx); sg_free_table(&pl022->sgt_tx); @@ -991,12 +991,12 @@ static int configure_dma(struct pl022 *pl022) pl022->cur_transfer->len, &pl022->sgt_tx); /* Map DMA buffers */ - sglen = dma_map_sg(&pl022->adev->dev, pl022->sgt_rx.sgl, + sglen = dma_map_sg(rxchan->device->dev, pl022->sgt_rx.sgl, pl022->sgt_rx.nents, DMA_FROM_DEVICE); if (!sglen) goto err_rx_sgmap; - sglen = dma_map_sg(&pl022->adev->dev, pl022->sgt_tx.sgl, + sglen = dma_map_sg(txchan->device->dev, pl022->sgt_tx.sgl, pl022->sgt_tx.nents, DMA_TO_DEVICE); if (!sglen) goto err_tx_sgmap; @@ -1040,10 +1040,10 @@ err_txdesc: txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0); err_rxdesc: rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0); - dma_unmap_sg(&pl022->adev->dev, pl022->sgt_tx.sgl, + dma_unmap_sg(txchan->device->dev, pl022->sgt_tx.sgl, pl022->sgt_tx.nents, DMA_TO_DEVICE); err_tx_sgmap: - dma_unmap_sg(&pl022->adev->dev, pl022->sgt_rx.sgl, + dma_unmap_sg(rxchan->device->dev, pl022->sgt_rx.sgl, pl022->sgt_tx.nents, DMA_FROM_DEVICE); err_rx_sgmap: sg_free_table(&pl022->sgt_tx); -- cgit v1.2.2 From 082086f2ce53c69260396e977d29972128def1d7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Dec 2010 23:13:37 +0100 Subject: spi/pl022: pass the returned sglen to the DMA engine The sglen return by the dma_map_sg() should be passed to the DMA engine, not the one passed in. If we one day have a DMA mapper that can coalesce entries, this will bug due to a too large number of entries being passed in. Reported-by: Russell King Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index e29751af5c7b..36ec1327a19b 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -917,7 +917,7 @@ static int configure_dma(struct pl022 *pl022) }; unsigned int pages; int ret; - int sglen; + int rx_sglen, tx_sglen; struct dma_chan *rxchan = pl022->dma_rx_channel; struct dma_chan *txchan = pl022->dma_tx_channel; struct dma_async_tx_descriptor *rxdesc; @@ -991,20 +991,20 @@ static int configure_dma(struct pl022 *pl022) pl022->cur_transfer->len, &pl022->sgt_tx); /* Map DMA buffers */ - sglen = dma_map_sg(rxchan->device->dev, pl022->sgt_rx.sgl, + rx_sglen = dma_map_sg(rxchan->device->dev, pl022->sgt_rx.sgl, pl022->sgt_rx.nents, DMA_FROM_DEVICE); - if (!sglen) + if (!rx_sglen) goto err_rx_sgmap; - sglen = dma_map_sg(txchan->device->dev, pl022->sgt_tx.sgl, + tx_sglen = dma_map_sg(txchan->device->dev, pl022->sgt_tx.sgl, pl022->sgt_tx.nents, DMA_TO_DEVICE); - if (!sglen) + if (!tx_sglen) goto err_tx_sgmap; /* Send both scatterlists */ rxdesc = rxchan->device->device_prep_slave_sg(rxchan, pl022->sgt_rx.sgl, - pl022->sgt_rx.nents, + rx_sglen, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!rxdesc) @@ -1012,7 +1012,7 @@ static int configure_dma(struct pl022 *pl022) txdesc = txchan->device->device_prep_slave_sg(txchan, pl022->sgt_tx.sgl, - pl022->sgt_tx.nents, + tx_sglen, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!txdesc) -- cgit v1.2.2 From dec5a581eb5b7d1abc90885d897d2468f1e60b21 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Dec 2010 23:13:48 +0100 Subject: spi/pl022: convert busy flag to a bool Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 36ec1327a19b..4b618f0e44dd 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -369,7 +369,7 @@ struct pl022 { struct work_struct pump_messages; spinlock_t queue_lock; struct list_head queue; - int busy; + bool busy; int run; /* Message transfer pump */ struct tasklet_struct pump_transfers; @@ -1461,7 +1461,7 @@ static void pump_messages(struct work_struct *work) /* Lock queue and check for queue work */ spin_lock_irqsave(&pl022->queue_lock, flags); if (list_empty(&pl022->queue) || pl022->run == QUEUE_STOPPED) { - pl022->busy = 0; + pl022->busy = false; spin_unlock_irqrestore(&pl022->queue_lock, flags); return; } @@ -1475,7 +1475,7 @@ static void pump_messages(struct work_struct *work) list_entry(pl022->queue.next, struct spi_message, queue); list_del_init(&pl022->cur_msg->queue); - pl022->busy = 1; + pl022->busy = true; spin_unlock_irqrestore(&pl022->queue_lock, flags); /* Initial message state */ @@ -1508,7 +1508,7 @@ static int __init init_queue(struct pl022 *pl022) spin_lock_init(&pl022->queue_lock); pl022->run = QUEUE_STOPPED; - pl022->busy = 0; + pl022->busy = false; tasklet_init(&pl022->pump_transfers, pump_transfers, (unsigned long)pl022); -- cgit v1.2.2 From 5e8b821de333f472d33e3052a8dd1c43bf3ce433 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Dec 2010 23:13:59 +0100 Subject: spi/pl022: convert running variable This variable is a bool but defined an int and defined completely backwards. This makes the code more readable. Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 4b618f0e44dd..a2a5921c730a 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -252,11 +252,6 @@ #define STATE_DONE ((void *) 2) #define STATE_ERROR ((void *) -1) -/* - * Queue State - */ -#define QUEUE_RUNNING (0) -#define QUEUE_STOPPED (1) /* * SSP State - Whether Enabled or Disabled */ @@ -344,7 +339,7 @@ struct vendor_data { * @lock: spinlock to syncronise access to driver data * @workqueue: a workqueue on which any spi_message request is queued * @busy: workqueue is busy - * @run: workqueue is running + * @running: workqueue is running * @pump_transfers: Tasklet used in Interrupt Transfer mode * @cur_msg: Pointer to current spi_message being processed * @cur_transfer: Pointer to current spi_transfer @@ -370,7 +365,7 @@ struct pl022 { spinlock_t queue_lock; struct list_head queue; bool busy; - int run; + bool running; /* Message transfer pump */ struct tasklet_struct pump_transfers; struct spi_message *cur_msg; @@ -1460,7 +1455,7 @@ static void pump_messages(struct work_struct *work) /* Lock queue and check for queue work */ spin_lock_irqsave(&pl022->queue_lock, flags); - if (list_empty(&pl022->queue) || pl022->run == QUEUE_STOPPED) { + if (list_empty(&pl022->queue) || !pl022->running) { pl022->busy = false; spin_unlock_irqrestore(&pl022->queue_lock, flags); return; @@ -1507,7 +1502,7 @@ static int __init init_queue(struct pl022 *pl022) INIT_LIST_HEAD(&pl022->queue); spin_lock_init(&pl022->queue_lock); - pl022->run = QUEUE_STOPPED; + pl022->running = false; pl022->busy = false; tasklet_init(&pl022->pump_transfers, @@ -1529,12 +1524,12 @@ static int start_queue(struct pl022 *pl022) spin_lock_irqsave(&pl022->queue_lock, flags); - if (pl022->run == QUEUE_RUNNING || pl022->busy) { + if (pl022->running || pl022->busy) { spin_unlock_irqrestore(&pl022->queue_lock, flags); return -EBUSY; } - pl022->run = QUEUE_RUNNING; + pl022->running = true; pl022->cur_msg = NULL; pl022->cur_transfer = NULL; pl022->cur_chip = NULL; @@ -1566,7 +1561,8 @@ static int stop_queue(struct pl022 *pl022) if (!list_empty(&pl022->queue) || pl022->busy) status = -EBUSY; - else pl022->run = QUEUE_STOPPED; + else + pl022->running = false; spin_unlock_irqrestore(&pl022->queue_lock, flags); @@ -1684,7 +1680,7 @@ static int pl022_transfer(struct spi_device *spi, struct spi_message *msg) spin_lock_irqsave(&pl022->queue_lock, flags); - if (pl022->run == QUEUE_STOPPED) { + if (!pl022->running) { spin_unlock_irqrestore(&pl022->queue_lock, flags); return -ESHUTDOWN; } @@ -1693,7 +1689,7 @@ static int pl022_transfer(struct spi_device *spi, struct spi_message *msg) msg->state = STATE_START; list_add_tail(&msg->queue, &pl022->queue); - if (pl022->run == QUEUE_RUNNING && !pl022->busy) + if (pl022->running && !pl022->busy) queue_work(pl022->workqueue, &pl022->pump_messages); spin_unlock_irqrestore(&pl022->queue_lock, flags); -- cgit v1.2.2 From ebf45b7d029eb065819bfede8b30455630d76c68 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Fri, 24 Dec 2010 13:59:09 +0800 Subject: spi/dw_spi: Fix too short timeout in spi polling loop The SPI polling loop timeout only works with HZ=100 as the loop was actually too short. Also add appropriate cpu_relax() in the busy wait loops... Signed-off-by: Arjan van de Ven Signed-off-by: Alan Cox Signed-off-by: Feng Tang Signed-off-by: Grant Likely --- drivers/spi/dw_spi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 0838c79861e4..25238a82e45e 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -164,20 +164,23 @@ 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 + 1 + usecs_to_jiffies(1000); + unsigned long end = jiffies + 1 + usecs_to_jiffies(5000); while (time_before(jiffies, end)) { if (!(dw_readw(dws, sr) & SR_BUSY)) return; + cpu_relax(); } dev_err(&dws->master->dev, - "DW SPI: Status keeps busy for 1000us after a read/write!\n"); + "DW SPI: Status keeps busy for 5000us after a read/write!\n"); } static void flush(struct dw_spi *dws) { - while (dw_readw(dws, sr) & SR_RF_NOT_EMPT) + while (dw_readw(dws, sr) & SR_RF_NOT_EMPT) { dw_readw(dws, dr); + cpu_relax(); + } wait_till_not_busy(dws); } -- cgit v1.2.2 From 79290a2aa2fd1c179a285218472092475630dc0e Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Fri, 24 Dec 2010 13:59:10 +0800 Subject: spi/dw_spi: change to EXPORT_SYMBOL_GPL for exported APIs Signed-off-by: Feng Tang Signed-off-by: Grant Likely --- drivers/spi/dw_spi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 25238a82e45e..b50bf5ba873f 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -941,7 +941,7 @@ err_free_master: exit: return ret; } -EXPORT_SYMBOL(dw_spi_add_host); +EXPORT_SYMBOL_GPL(dw_spi_add_host); void __devexit dw_spi_remove_host(struct dw_spi *dws) { @@ -965,7 +965,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); +EXPORT_SYMBOL_GPL(dw_spi_remove_host); int dw_spi_suspend_host(struct dw_spi *dws) { @@ -978,7 +978,7 @@ int dw_spi_suspend_host(struct dw_spi *dws) spi_set_clk(dws, 0); return ret; } -EXPORT_SYMBOL(dw_spi_suspend_host); +EXPORT_SYMBOL_GPL(dw_spi_suspend_host); int dw_spi_resume_host(struct dw_spi *dws) { @@ -990,7 +990,7 @@ int dw_spi_resume_host(struct dw_spi *dws) dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret); return ret; } -EXPORT_SYMBOL(dw_spi_resume_host); +EXPORT_SYMBOL_GPL(dw_spi_resume_host); MODULE_AUTHOR("Feng Tang "); MODULE_DESCRIPTION("Driver for DesignWare SPI controller core"); -- cgit v1.2.2 From 7063c0d942a1af2993531fbe52b4c74c1db818c4 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Fri, 24 Dec 2010 13:59:11 +0800 Subject: spi/dw_spi: add DMA support dw_spi driver in upstream only supports PIO mode, and this patch will support it to cowork with the Designware dma controller used on Intel Moorestown platform, at the same time it provides a general framework to support dw_spi core to cowork with dma controllers on other platforms It has been tested with a Option GTM501L 3G modem and Infenion 60x60 modem. To use DMA mode, DMA controller 2 of Moorestown has to be enabled Also change the dma interface suggested by Linus Walleij. Acked-by: Linus Walleij Signed-off-by: Feng Tang [Typo fix and renames to match intel_mid_dma renaming] Signed-off-by: Vinod Koul Signed-off-by: Alan Cox Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 4 + drivers/spi/Makefile | 3 +- drivers/spi/dw_spi.c | 33 ++++--- drivers/spi/dw_spi_mid.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/dw_spi_pci.c | 20 +++-- 5 files changed, 264 insertions(+), 19 deletions(-) create mode 100644 drivers/spi/dw_spi_mid.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 665d03d4e022..caa2e84cc0a6 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -382,6 +382,10 @@ config SPI_DW_PCI tristate "PCI interface driver for DW SPI core" depends on SPI_DESIGNWARE && PCI +config SPI_DW_MID_DMA + bool "DMA support for DW SPI controller on Intel Moorestown platform" + depends on SPI_DW_PCI && INTEL_MID_DMAC + config SPI_DW_MMIO tristate "Memory-mapped io interface driver for DW SPI core" depends on SPI_DESIGNWARE && HAVE_CLK diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 02dad4ae412d..7ec375b6c0c3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -17,7 +17,8 @@ obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += coldfire_qspi.o obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o obj-$(CONFIG_SPI_DESIGNWARE) += dw_spi.o -obj-$(CONFIG_SPI_DW_PCI) += dw_spi_pci.o +obj-$(CONFIG_SPI_DW_PCI) += dw_spi_midpci.o +dw_spi_midpci-objs := dw_spi_pci.o dw_spi_mid.o obj-$(CONFIG_SPI_DW_MMIO) += dw_spi_mmio.o obj-$(CONFIG_SPI_EP93XX) += ep93xx_spi.o obj-$(CONFIG_SPI_GPIO) += spi_gpio.o diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index b50bf5ba873f..497ecb3ab83f 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -288,8 +288,10 @@ static void *next_transfer(struct dw_spi *dws) */ static int map_dma_buffers(struct dw_spi *dws) { - if (!dws->cur_msg->is_dma_mapped || !dws->dma_inited - || !dws->cur_chip->enable_dma) + if (!dws->cur_msg->is_dma_mapped + || !dws->dma_inited + || !dws->cur_chip->enable_dma + || !dws->dma_ops) return 0; if (dws->cur_transfer->tx_dma) @@ -341,7 +343,7 @@ static void int_error_stop(struct dw_spi *dws, const char *msg) tasklet_schedule(&dws->pump_transfers); } -static void transfer_complete(struct dw_spi *dws) +void dw_spi_xfer_done(struct dw_spi *dws) { /* Update total byte transfered return count actual bytes read */ dws->cur_msg->actual_length += dws->len; @@ -356,6 +358,7 @@ static void transfer_complete(struct dw_spi *dws) } else tasklet_schedule(&dws->pump_transfers); } +EXPORT_SYMBOL_GPL(dw_spi_xfer_done); static irqreturn_t interrupt_transfer(struct dw_spi *dws) { @@ -387,7 +390,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) if (dws->tx_end > dws->tx) spi_umask_intr(dws, SPI_INT_TXEI); else - transfer_complete(dws); + dw_spi_xfer_done(dws); } return IRQ_HANDLED; @@ -422,11 +425,7 @@ static void poll_transfer(struct dw_spi *dws) */ dws->read(dws); - transfer_complete(dws); -} - -static void dma_transfer(struct dw_spi *dws, int cs_change) -{ + dw_spi_xfer_done(dws); } static void pump_transfers(unsigned long data) @@ -608,7 +607,7 @@ static void pump_transfers(unsigned long data) } if (dws->dma_mapped) - dma_transfer(dws, cs_change); + dws->dma_ops->dma_transfer(dws, cs_change); if (chip->poll_mode) poll_transfer(dws); @@ -904,11 +903,17 @@ int __devinit dw_spi_add_host(struct dw_spi *dws) master->setup = dw_spi_setup; master->transfer = dw_spi_transfer; - dws->dma_inited = 0; - /* Basic HW init */ spi_hw_init(dws); + if (dws->dma_ops && dws->dma_ops->dma_init) { + ret = dws->dma_ops->dma_init(dws); + if (ret) { + dev_warn(&master->dev, "DMA init failed\n"); + dws->dma_inited = 0; + } + } + /* Initial and start queue */ ret = init_queue(dws); if (ret) { @@ -933,6 +938,8 @@ int __devinit dw_spi_add_host(struct dw_spi *dws) err_queue_alloc: destroy_queue(dws); + if (dws->dma_ops && dws->dma_ops->dma_exit) + dws->dma_ops->dma_exit(dws); err_diable_hw: spi_enable_chip(dws, 0); free_irq(dws->irq, dws); @@ -957,6 +964,8 @@ void __devexit dw_spi_remove_host(struct dw_spi *dws) dev_err(&dws->master->dev, "dw_spi_remove: workqueue will not " "complete, message memory not freed\n"); + if (dws->dma_ops && dws->dma_ops->dma_exit) + dws->dma_ops->dma_exit(dws); spi_enable_chip(dws, 0); /* Disable clk */ spi_set_clk(dws, 0); diff --git a/drivers/spi/dw_spi_mid.c b/drivers/spi/dw_spi_mid.c new file mode 100644 index 000000000000..c91c966e0717 --- /dev/null +++ b/drivers/spi/dw_spi_mid.c @@ -0,0 +1,223 @@ +/* + * dw_spi_mid.c - special handling for DW core on Intel MID platform + * + * Copyright (c) 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SPI_DW_MID_DMA +#include +#include + +struct mid_dma { + struct intel_mid_dma_slave dmas_tx; + struct intel_mid_dma_slave dmas_rx; +}; + +static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) +{ + struct dw_spi *dws = param; + + return dws->dmac && (&dws->dmac->dev == chan->device->dev); +} + +static int mid_spi_dma_init(struct dw_spi *dws) +{ + struct mid_dma *dw_dma = dws->dma_priv; + struct intel_mid_dma_slave *rxs, *txs; + dma_cap_mask_t mask; + + /* + * Get pci device for DMA controller, currently it could only + * be the DMA controller of either Moorestown or Medfield + */ + dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0813, NULL); + if (!dws->dmac) + dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL); + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* 1. Init rx channel */ + dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws); + if (!dws->rxchan) + goto err_exit; + rxs = &dw_dma->dmas_rx; + rxs->hs_mode = LNW_DMA_HW_HS; + rxs->cfg_mode = LNW_DMA_PER_TO_MEM; + dws->rxchan->private = rxs; + + /* 2. Init tx channel */ + dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws); + if (!dws->txchan) + goto free_rxchan; + txs = &dw_dma->dmas_tx; + txs->hs_mode = LNW_DMA_HW_HS; + txs->cfg_mode = LNW_DMA_MEM_TO_PER; + dws->txchan->private = txs; + + dws->dma_inited = 1; + return 0; + +free_rxchan: + dma_release_channel(dws->rxchan); +err_exit: + return -1; + +} + +static void mid_spi_dma_exit(struct dw_spi *dws) +{ + dma_release_channel(dws->txchan); + dma_release_channel(dws->rxchan); +} + +/* + * dws->dma_chan_done is cleared before the dma transfer starts, + * callback for rx/tx channel will each increment it by 1. + * Reaching 2 means the whole spi transaction is done. + */ +static void dw_spi_dma_done(void *arg) +{ + struct dw_spi *dws = arg; + + if (++dws->dma_chan_done != 2) + return; + dw_spi_xfer_done(dws); +} + +static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) +{ + struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL; + struct dma_chan *txchan, *rxchan; + struct dma_slave_config txconf, rxconf; + u16 dma_ctrl = 0; + + /* 1. setup DMA related registers */ + if (cs_change) { + spi_enable_chip(dws, 0); + dw_writew(dws, dmardlr, 0xf); + dw_writew(dws, dmatdlr, 0x10); + if (dws->tx_dma) + dma_ctrl |= 0x2; + if (dws->rx_dma) + dma_ctrl |= 0x1; + dw_writew(dws, dmacr, dma_ctrl); + spi_enable_chip(dws, 1); + } + + dws->dma_chan_done = 0; + txchan = dws->txchan; + rxchan = dws->rxchan; + + /* 2. Prepare the TX dma transfer */ + txconf.direction = DMA_TO_DEVICE; + txconf.dst_addr = dws->dma_addr; + txconf.dst_maxburst = LNW_DMA_MSIZE_16; + txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + txchan->device->device_control(txchan, DMA_SLAVE_CONFIG, + (unsigned long) &txconf); + + memset(&dws->tx_sgl, 0, sizeof(dws->tx_sgl)); + dws->tx_sgl.dma_address = dws->tx_dma; + dws->tx_sgl.length = dws->len; + + txdesc = txchan->device->device_prep_slave_sg(txchan, + &dws->tx_sgl, + 1, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP); + txdesc->callback = dw_spi_dma_done; + txdesc->callback_param = dws; + + /* 3. Prepare the RX dma transfer */ + rxconf.direction = DMA_FROM_DEVICE; + rxconf.src_addr = dws->dma_addr; + rxconf.src_maxburst = LNW_DMA_MSIZE_16; + rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG, + (unsigned long) &rxconf); + + memset(&dws->rx_sgl, 0, sizeof(dws->rx_sgl)); + dws->rx_sgl.dma_address = dws->rx_dma; + dws->rx_sgl.length = dws->len; + + rxdesc = rxchan->device->device_prep_slave_sg(rxchan, + &dws->rx_sgl, + 1, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP); + rxdesc->callback = dw_spi_dma_done; + rxdesc->callback_param = dws; + + /* rx must be started before tx due to spi instinct */ + rxdesc->tx_submit(rxdesc); + txdesc->tx_submit(txdesc); + return 0; +} + +static struct dw_spi_dma_ops mid_dma_ops = { + .dma_init = mid_spi_dma_init, + .dma_exit = mid_spi_dma_exit, + .dma_transfer = mid_spi_dma_transfer, +}; +#endif + +/* Some specific info for SPI0 controller on Moorestown */ + +/* HW info for MRST CLk Control Unit, one 32b reg */ +#define MRST_SPI_CLK_BASE 100000000 /* 100m */ +#define MRST_CLK_SPI0_REG 0xff11d86c +#define CLK_SPI_BDIV_OFFSET 0 +#define CLK_SPI_BDIV_MASK 0x00000007 +#define CLK_SPI_CDIV_OFFSET 9 +#define CLK_SPI_CDIV_MASK 0x00000e00 +#define CLK_SPI_DISABLE_OFFSET 8 + +int dw_spi_mid_init(struct dw_spi *dws) +{ + u32 *clk_reg, clk_cdiv; + + clk_reg = ioremap_nocache(MRST_CLK_SPI0_REG, 16); + if (!clk_reg) + return -ENOMEM; + + /* get SPI controller operating freq info */ + clk_cdiv = (readl(clk_reg) & CLK_SPI_CDIV_MASK) >> CLK_SPI_CDIV_OFFSET; + dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1); + iounmap(clk_reg); + + dws->num_cs = 16; + dws->fifo_len = 40; /* FIFO has 40 words buffer */ + +#ifdef CONFIG_SPI_DW_MID_DMA + dws->dma_priv = kzalloc(sizeof(struct mid_dma), GFP_KERNEL); + if (!dws->dma_priv) + return -ENOMEM; + dws->dma_ops = &mid_dma_ops; +#endif + return 0; +} diff --git a/drivers/spi/dw_spi_pci.c b/drivers/spi/dw_spi_pci.c index 1f52755dc878..49ec3aa1219f 100644 --- a/drivers/spi/dw_spi_pci.c +++ b/drivers/spi/dw_spi_pci.c @@ -1,5 +1,5 @@ /* - * mrst_spi_pci.c - PCI interface driver for DW SPI Core + * dw_spi_pci.c - PCI interface driver for DW SPI Core * * Copyright (c) 2009, Intel Corporation. * @@ -26,8 +26,8 @@ #define DRIVER_NAME "dw_spi_pci" struct dw_spi_pci { - struct pci_dev *pdev; - struct dw_spi dws; + struct pci_dev *pdev; + struct dw_spi dws; }; static int __devinit spi_pci_probe(struct pci_dev *pdev, @@ -72,9 +72,17 @@ static int __devinit spi_pci_probe(struct pci_dev *pdev, dws->parent_dev = &pdev->dev; dws->bus_num = 0; dws->num_cs = 4; - dws->max_freq = 25000000; /* for Moorestwon */ dws->irq = pdev->irq; - dws->fifo_len = 40; /* FIFO has 40 words buffer */ + + /* + * Specific handling for Intel MID paltforms, like dma setup, + * clock rate, FIFO depth. + */ + if (pdev->device == 0x0800) { + ret = dw_spi_mid_init(dws); + if (ret) + goto err_unmap; + } ret = dw_spi_add_host(dws); if (ret) @@ -140,7 +148,7 @@ static int spi_resume(struct pci_dev *pdev) #endif static const struct pci_device_id pci_ids[] __devinitdata = { - /* Intel Moorestown platform SPI controller 0 */ + /* Intel MID platform SPI controller 0 */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) }, {}, }; -- cgit v1.2.2 From e6289d63a6f39237a027dcee46366ba158cb8406 Mon Sep 17 00:00:00 2001 From: Mingkai Hu Date: Tue, 21 Dec 2010 09:26:07 +0800 Subject: spi/fsl_espi: change the read behaviour of the SPIRF The user must read N bytes of SPIRF (1 <= N <= 4) that do not exceed the amount of data in the receive FIFO, so read the SPIRF byte by byte when the data in receive FIFO is less than 4 bytes. On Simics, when read N bytes that exceed the amount of data in receive FIFO, we can't read the data out, that is we can't clear the rx FIFO, then the CPU will loop on the espi rx interrupt. Signed-off-by: Mingkai Hu Signed-off-by: Grant Likely --- drivers/spi/spi_fsl_espi.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_fsl_espi.c b/drivers/spi/spi_fsl_espi.c index e3b4f6451966..ae789262c981 100644 --- a/drivers/spi/spi_fsl_espi.c +++ b/drivers/spi/spi_fsl_espi.c @@ -507,16 +507,29 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) /* We need handle RX first */ if (events & SPIE_NE) { - u32 rx_data; + u32 rx_data, tmp; + u8 rx_data_8; /* Spin until RX is done */ while (SPIE_RXCNT(events) < min(4, mspi->len)) { cpu_relax(); events = mpc8xxx_spi_read_reg(®_base->event); } - mspi->len -= 4; - rx_data = mpc8xxx_spi_read_reg(®_base->receive); + if (mspi->len >= 4) { + rx_data = mpc8xxx_spi_read_reg(®_base->receive); + } else { + tmp = mspi->len; + rx_data = 0; + while (tmp--) { + rx_data_8 = in_8((u8 *)®_base->receive); + rx_data |= (rx_data_8 << (tmp * 8)); + } + + rx_data <<= (4 - mspi->len) * 8; + } + + mspi->len -= 4; if (mspi->rx) mspi->get_rx(rx_data, mspi); -- cgit v1.2.2 From 0dd2c96f19b0cffaeb437e50fa5da195920c6e78 Mon Sep 17 00:00:00 2001 From: Mingkai Hu Date: Tue, 21 Dec 2010 09:27:02 +0800 Subject: spi/fsl_espi: fix wrong setting of the address in the command buffer Or else we can't operate on the right address when the trans length is greater than 65535. Signed-off-by: Mingkai Hu Signed-off-by: Grant Likely --- drivers/spi/spi_fsl_espi.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_fsl_espi.c b/drivers/spi/spi_fsl_espi.c index ae789262c981..a99e2333b949 100644 --- a/drivers/spi/spi_fsl_espi.c +++ b/drivers/spi/spi_fsl_espi.c @@ -258,18 +258,18 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) return mpc8xxx_spi->count; } -static void fsl_espi_addr2cmd(unsigned int addr, u8 *cmd) +static inline void fsl_espi_addr2cmd(unsigned int addr, u8 *cmd) { - if (cmd[1] && cmd[2] && cmd[3]) { + if (cmd) { cmd[1] = (u8)(addr >> 16); cmd[2] = (u8)(addr >> 8); cmd[3] = (u8)(addr >> 0); } } -static unsigned int fsl_espi_cmd2addr(u8 *cmd) +static inline unsigned int fsl_espi_cmd2addr(u8 *cmd) { - if (cmd[1] && cmd[2] && cmd[3]) + if (cmd) return cmd[1] << 16 | cmd[2] << 8 | cmd[3] << 0; return 0; @@ -395,9 +395,11 @@ static void fsl_espi_rw_trans(struct spi_message *m, } } - addr = fsl_espi_cmd2addr(local_buf); - addr += pos; - fsl_espi_addr2cmd(addr, local_buf); + if (pos > 0) { + addr = fsl_espi_cmd2addr(local_buf); + addr += pos; + fsl_espi_addr2cmd(addr, local_buf); + } espi_trans->n_tx = n_tx; espi_trans->n_rx = trans_len; -- cgit v1.2.2 From 4bdac7da5237170b1392f39ebee99d235043fad8 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 17 Dec 2010 15:33:58 +0100 Subject: spi/mpc52xx-spi: fix annotation for remove()-pointer Signed-off-by: Wolfram Sang Signed-off-by: Grant Likely --- drivers/spi/mpc52xx_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c index ec9f0b1bf864..84439f655601 100644 --- a/drivers/spi/mpc52xx_spi.c +++ b/drivers/spi/mpc52xx_spi.c @@ -563,7 +563,7 @@ static struct of_platform_driver mpc52xx_spi_of_driver = { .of_match_table = mpc52xx_spi_match, }, .probe = mpc52xx_spi_probe, - .remove = __exit_p(mpc52xx_spi_remove), + .remove = __devexit_p(mpc52xx_spi_remove), }; static int __init mpc52xx_spi_init(void) -- cgit v1.2.2 From 5aa68b85951aec91d6a955d1de861325fc9a3ba1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 17 Dec 2010 15:44:00 +0100 Subject: spi/mpc52xx-psc-spi: move probe/remove to proper sections Probe/remove should be in __devinit/__devexit. Found by a section-mismatch warning. Also, copy do_remove() over to remove(). Signed-off-by: Wolfram Sang Signed-off-by: Grant Likely --- drivers/spi/mpc52xx_psc_spi.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c index 983fbbfce76e..8a904c1c8485 100644 --- a/drivers/spi/mpc52xx_psc_spi.c +++ b/drivers/spi/mpc52xx_psc_spi.c @@ -363,7 +363,7 @@ static irqreturn_t mpc52xx_psc_spi_isr(int irq, void *dev_id) } /* bus_num is used only for the case dev->platform_data == NULL */ -static int __init mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr, +static int __devinit mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr, u32 size, unsigned int irq, s16 bus_num) { struct fsl_spi_platform_data *pdata = dev->platform_data; @@ -450,22 +450,7 @@ free_master: return ret; } -static int __exit mpc52xx_psc_spi_do_remove(struct device *dev) -{ - struct spi_master *master = dev_get_drvdata(dev); - struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); - - flush_workqueue(mps->workqueue); - destroy_workqueue(mps->workqueue); - spi_unregister_master(master); - free_irq(mps->irq, mps); - if (mps->psc) - iounmap(mps->psc); - - return 0; -} - -static int __init mpc52xx_psc_spi_of_probe(struct platform_device *op, +static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op, const struct of_device_id *match) { const u32 *regaddr_p; @@ -495,9 +480,19 @@ static int __init mpc52xx_psc_spi_of_probe(struct platform_device *op, irq_of_parse_and_map(op->dev.of_node, 0), id); } -static int __exit mpc52xx_psc_spi_of_remove(struct platform_device *op) +static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op) { - return mpc52xx_psc_spi_do_remove(&op->dev); + struct spi_master *master = dev_get_drvdata(&op->dev); + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); + + flush_workqueue(mps->workqueue); + destroy_workqueue(mps->workqueue); + spi_unregister_master(master); + free_irq(mps->irq, mps); + if (mps->psc) + iounmap(mps->psc); + + return 0; } static const struct of_device_id mpc52xx_psc_spi_of_match[] = { @@ -510,7 +505,7 @@ MODULE_DEVICE_TABLE(of, mpc52xx_psc_spi_of_match); static struct of_platform_driver mpc52xx_psc_spi_of_driver = { .probe = mpc52xx_psc_spi_of_probe, - .remove = __exit_p(mpc52xx_psc_spi_of_remove), + .remove = __devexit_p(mpc52xx_psc_spi_of_remove), .driver = { .name = "mpc52xx-psc-spi", .owner = THIS_MODULE, -- cgit v1.2.2 From 3ae22e8c8ac39daf88ae32f047fb23825be7c646 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 25 Dec 2010 15:32:27 +0100 Subject: spi / PM: Support dev_pm_ops Allow SPI drivers to use runtime PM and other dev_pm_ops features by implementing dev_pm_ops for the bus. The existing bus specific suspend and resume functions will be called if a driver does not provide dev_pm_ops allowing for transition to the new model. Signed-off-by: Mark Brown Acked-by: Grant Likely Signed-off-by: Rafael J. Wysocki --- drivers/spi/spi.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b02d0cbce890..34bb17f03019 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -28,6 +28,7 @@ #include #include #include +#include static void spidev_release(struct device *dev) { @@ -100,9 +101,8 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } -#ifdef CONFIG_PM - -static int spi_suspend(struct device *dev, pm_message_t message) +#ifdef CONFIG_PM_SLEEP +static int spi_legacy_suspend(struct device *dev, pm_message_t message) { int value = 0; struct spi_driver *drv = to_spi_driver(dev->driver); @@ -117,7 +117,7 @@ static int spi_suspend(struct device *dev, pm_message_t message) return value; } -static int spi_resume(struct device *dev) +static int spi_legacy_resume(struct device *dev) { int value = 0; struct spi_driver *drv = to_spi_driver(dev->driver); @@ -132,18 +132,94 @@ static int spi_resume(struct device *dev) return value; } +static int spi_pm_suspend(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_suspend(dev); + else + return spi_legacy_suspend(dev, PMSG_SUSPEND); +} + +static int spi_pm_resume(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_resume(dev); + else + return spi_legacy_resume(dev); +} + +static int spi_pm_freeze(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_freeze(dev); + else + return spi_legacy_suspend(dev, PMSG_FREEZE); +} + +static int spi_pm_thaw(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_thaw(dev); + else + return spi_legacy_resume(dev); +} + +static int spi_pm_poweroff(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_poweroff(dev); + else + return spi_legacy_suspend(dev, PMSG_HIBERNATE); +} + +static int spi_pm_restore(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_restore(dev); + else + return spi_legacy_resume(dev); +} #else -#define spi_suspend NULL -#define spi_resume NULL +#define spi_pm_suspend NULL +#define spi_pm_resume NULL +#define spi_pm_freeze NULL +#define spi_pm_thaw NULL +#define spi_pm_poweroff NULL +#define spi_pm_restore NULL #endif +static const struct dev_pm_ops spi_pm = { + .suspend = spi_pm_suspend, + .resume = spi_pm_resume, + .freeze = spi_pm_freeze, + .thaw = spi_pm_thaw, + .poweroff = spi_pm_poweroff, + .restore = spi_pm_restore, + SET_RUNTIME_PM_OPS( + pm_generic_runtime_suspend, + pm_generic_runtime_resume, + pm_generic_runtime_idle + ) +}; + struct bus_type spi_bus_type = { .name = "spi", .dev_attrs = spi_dev_attrs, .match = spi_match_device, .uevent = spi_uevent, - .suspend = spi_suspend, - .resume = spi_resume, + .pm = &spi_pm, }; EXPORT_SYMBOL_GPL(spi_bus_type); -- cgit v1.2.2 From 42ce7fd6319bed8ecb26d656c476365da46b29e9 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 29 Dec 2010 11:52:53 +0100 Subject: spi/omap2_mcspi.c: Force CS to be in inactive state after off-mode transition When SPI wake up from OFF mode, CS is in the wrong state: force it to the inactive state. During the system life, I monitored the CS behavior using a oscilloscope. I also activated debug in omap2_mcspi, so I saw when driver disable the clocks and restore context when device is not used.Each time the CS was in the correct state. It was only when system was put suspend to ram with off-mode activated that on resume the CS was in wrong state( ie activated). Changelog: * Change from v1 to v2: - Rebase on linus/master (after 2.6.37-rc1) - Do some clean-up and fix indentation on both patches - Add more explanations for patch 2 * Change from v2 to v3: - Use directly resume function of spi_master instead of using function - from spi_device as Grant Likely pointed it out. - Force this transition explicitly for each CS used by a device. * Change from v3 to v4: - Patch clean-up according to Kevin Hilman and checkpatch. - Now force CS to be in inactive state only if it was inactive when it was suspended. * Change from v4 to v5: - Rebase on linus/master (after 2.6.37-rc3) - Collapse some lines as pointed by Grant Likely - Fix a spelling * Change from v5 to v6: - Rebase on linus/master (after 2.6.37-rc7) - Use CONFIG_SUSPEND instead of CONFIG_PM - Didn't use legacy PM methods anymore. Instead, add a struct dev_pm_ops and add the resume method there. - Fix multi-line comment style * Change from v6 to v7: - Rebase on linus/master (after 2.6.37-rc8) - Drop an extra line Signed-off-by: Gregory CLEMENT Acked-by: David Brownell Reviewed-by: Kevin Hilman Signed-off-by: Grant Likely --- drivers/spi/omap2_mcspi.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index 2a651e61bfbf..951a160fc27f 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -1305,10 +1305,49 @@ static int __exit omap2_mcspi_remove(struct platform_device *pdev) /* work with hotplug and coldplug */ MODULE_ALIAS("platform:omap2_mcspi"); +#ifdef CONFIG_SUSPEND +/* + * When SPI wake up from off-mode, CS is in activate state. If it was in + * unactive state when driver was suspend, then force it to unactive state at + * wake up. + */ +static int omap2_mcspi_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct omap2_mcspi *mcspi = spi_master_get_devdata(master); + struct omap2_mcspi_cs *cs; + + omap2_mcspi_enable_clocks(mcspi); + list_for_each_entry(cs, &omap2_mcspi_ctx[master->bus_num - 1].cs, + node) { + if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) { + + /* + * We need to toggle CS state for OMAP take this + * change in account. + */ + MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 1); + __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); + MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 0); + __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); + } + } + omap2_mcspi_disable_clocks(mcspi); + return 0; +} +#else +#define omap2_mcspi_resume NULL +#endif + +static const struct dev_pm_ops omap2_mcspi_pm_ops = { + .resume = omap2_mcspi_resume, +}; + static struct platform_driver omap2_mcspi_driver = { .driver = { .name = "omap2_mcspi", .owner = THIS_MODULE, + .pm = &omap2_mcspi_pm_ops }, .remove = __exit_p(omap2_mcspi_remove), }; -- cgit v1.2.2 From 477ca3ad6ac5cdbd5bd40941fc22c6eedc9aa90d Mon Sep 17 00:00:00 2001 From: Mingkai Hu Date: Wed, 1 Dec 2010 17:29:18 +0800 Subject: spi/fsl_espi: change the read behaviour of the SPIRF The user must read N bytes of SPIRF (1 <= N <= 4) that do not exceed the amount of data in the receive FIFO, so read the SPIRF byte by byte when the data in receive FIFO is less than 4 bytes. On Simics, when read N bytes that exceed the amout of data in receive FIFO, we can't read the data out, that is we can't clear the rx FIFO, then the CPU will loop on the espi rx interrupt. Signed-off-by: Mingkai Hu Signed-off-by: Grant Likely --- drivers/spi/spi_fsl_espi.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_fsl_espi.c b/drivers/spi/spi_fsl_espi.c index e3b4f6451966..ae789262c981 100644 --- a/drivers/spi/spi_fsl_espi.c +++ b/drivers/spi/spi_fsl_espi.c @@ -507,16 +507,29 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) /* We need handle RX first */ if (events & SPIE_NE) { - u32 rx_data; + u32 rx_data, tmp; + u8 rx_data_8; /* Spin until RX is done */ while (SPIE_RXCNT(events) < min(4, mspi->len)) { cpu_relax(); events = mpc8xxx_spi_read_reg(®_base->event); } - mspi->len -= 4; - rx_data = mpc8xxx_spi_read_reg(®_base->receive); + if (mspi->len >= 4) { + rx_data = mpc8xxx_spi_read_reg(®_base->receive); + } else { + tmp = mspi->len; + rx_data = 0; + while (tmp--) { + rx_data_8 = in_8((u8 *)®_base->receive); + rx_data |= (rx_data_8 << (tmp * 8)); + } + + rx_data <<= (4 - mspi->len) * 8; + } + + mspi->len -= 4; if (mspi->rx) mspi->get_rx(rx_data, mspi); -- cgit v1.2.2 From 0bc463426ab5eb39e76df637b29a4f191d01b8a0 Mon Sep 17 00:00:00 2001 From: Jate Sujjavanich Date: Wed, 29 Sep 2010 09:44:32 -0400 Subject: spi/m68knommu: Coldfire QSPI platform support After grabbing a msg from the msgq, the mcfqspi_work function calls list_del_init on the mcfqspi->msgq which unintentionally deletes the rest of the list before it can be processed. If qspi call was made using spi_sync, this can result in a process hang. Signed-off-by: Jate Sujjavanich Acked-by: Steven King Signed-off-by: Grant Likely --- drivers/spi/coldfire_qspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/coldfire_qspi.c b/drivers/spi/coldfire_qspi.c index 052b3c7fa6a0..8856bcca9d29 100644 --- a/drivers/spi/coldfire_qspi.c +++ b/drivers/spi/coldfire_qspi.c @@ -317,7 +317,7 @@ static void mcfqspi_work(struct work_struct *work) msg = container_of(mcfqspi->msgq.next, struct spi_message, queue); - list_del_init(&mcfqspi->msgq); + list_del_init(&msg->queue); spin_unlock_irqrestore(&mcfqspi->lock, flags); spi = msg->spi; -- cgit v1.2.2 From 2f263d9d4f0432e4f7a708c95fab82f83ec14947 Mon Sep 17 00:00:00 2001 From: "Justin P. Mattock" Date: Thu, 30 Dec 2010 15:07:51 -0800 Subject: spi/dw_spi Typo change diable to disable. Signed-off-by: Justin P. Mattock Signed-off-by: Grant Likely --- drivers/spi/dw_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 497ecb3ab83f..22af77f98816 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -594,7 +594,7 @@ static void pump_transfers(unsigned long data) 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 */ + /* Set the interrupt mask, for poll mode just disable all int */ spi_mask_intr(dws, 0xff); if (imask) spi_umask_intr(dws, imask); -- cgit v1.2.2 From 77e58efd134b553d723fd963f436920a6aaaa6df Mon Sep 17 00:00:00 2001 From: "Justin P. Mattock" Date: Fri, 31 Dec 2010 09:50:31 -0800 Subject: spi/topcliff: Typo fix threhold to threshold Signed-off-by: Justin P. Mattock Signed-off-by: Grant Likely --- drivers/spi/spi_topcliff_pch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c index 56b758847c04..79e48d451137 100644 --- a/drivers/spi/spi_topcliff_pch.c +++ b/drivers/spi/spi_topcliff_pch.c @@ -677,7 +677,7 @@ static void pch_spi_set_ir(struct pch_spi_data *data) { /* enable interrupts */ if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) { - /* set receive threhold to PCH_RX_THOLD */ + /* set receive threshold to PCH_RX_THOLD */ pch_spi_setclr_reg(data->master, PCH_SPCR, PCH_RX_THOLD << SPCR_RFIC_FIELD, ~MASK_RFIC_SPCR_BITS); @@ -685,7 +685,7 @@ static void pch_spi_set_ir(struct pch_spi_data *data) pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_RFIE_BIT | SPCR_FIE_BIT, 0); } else { - /* set receive threhold to maximum */ + /* set receive threshold to maximum */ pch_spi_setclr_reg(data->master, PCH_SPCR, PCH_RX_THOLD_MAX << SPCR_TFIC_FIELD, ~MASK_TFIC_SPCR_BITS); -- cgit v1.2.2 From 735759389b7143f95ae7badc97511e7e4729e873 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 7 Jan 2011 15:26:01 +0100 Subject: spi/imx: correct the test on platform_get_irq() return value The test "if (spi_imx->irq <= 0)" is not testing the IRQ value, but the return value of platform_get_irq(). As platform_get_irq() can return an error (-ENXIO) or the IRQ value it found, the test should be "if (spi_imx->irq < 0)" [grant.likely: Note: In general, Linux irq number 0 should also mean no irq, but arm still allows devices to be assigned 0, and the imx platform uses 0 for one of the spi devices, so this patch is needed for the device to work] Signed-off-by: Richard Genoud Signed-off-by: Grant Likely --- drivers/spi/spi_imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 9010ef0385a8..9469564e6888 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -821,7 +821,7 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) } spi_imx->irq = platform_get_irq(pdev, 0); - if (spi_imx->irq <= 0) { + if (spi_imx->irq < 0) { ret = -EINVAL; goto out_iounmap; } -- cgit v1.2.2 From 07fe0351702b6f0c9749e80cdbcb758686b0fe9b Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Fri, 7 Jan 2011 15:49:20 +0000 Subject: spi/omap: Fix DMA API usage in OMAP MCSPI driver Running the latest kernel on the 4430SDP board with DMA API debugging enabled results in this: WARNING: at lib/dma-debug.c:803 check_unmap+0x19c/0x6f0() NULL NULL: DMA-API: device driver tries to free DMA memory it has not allocated [device address=0x000000008129901a] [size=260 bytes] Modules linked in: Backtrace: [] (dump_backtrace+0x0/0x10c) from [] (dump_stack+0x18/0x1c) r7:c1839dc0 r6:c0198578 r5:c0304b17 r4:00000323 [] (dump_stack+0x0/0x1c) from [] (warn_slowpath_common+0x58/0x70) [] (warn_slowpath_common+0x0/0x70) from [] (warn_slowpath_fmt+0x38/0x40) r8:c1839e40 r7:00000000 r6:00000104 r5:00000000 r4:8129901a [] (warn_slowpath_fmt+0x0/0x40) from [] (check_unmap+0x19c/0x6f0) r3:c03110de r2:c0304e6b [] (check_unmap+0x0/0x6f0) from [] (debug_dma_unmap_page+0x74/0x80) [] (debug_dma_unmap_page+0x0/0x80) from [] (omap2_mcspi_work+0x514/0xbf0) [] (omap2_mcspi_work+0x0/0xbf0) from [] (process_one_work+0x294/0x400) [] (process_one_work+0x0/0x400) from [] (worker_thread+0x220/0x3f8) [] (worker_thread+0x0/0x3f8) from [] (kthread+0x88/0x90) [] (kthread+0x0/0x90) from [] (do_exit+0x0/0x5fc) r7:00000013 r6:c005e924 r5:c0073848 r4:c1829ee0 ---[ end trace 1b75b31a2719ed20 ]--- I've no idea why this driver uses NULL for dma_unmap_single instead of the &spi->dev that is laying around just waiting to be used in that function - but it's an easy fix. Also replace this comment with a FIXME comment: /* Do DMA mapping "early" for better error reporting and * dcache use. Note that if dma_unmap_single() ever starts * to do real work on ARM, we'd need to clean up mappings * for previous transfers on *ALL* exits of this loop... */ as the comment is not true - we do work in dma_unmap() functions, particularly on ARMv6 and above. I've corrected the existing unmap functions but if any others are required they must be added ASAP. Signed-off-by: Russell King Acked-by: Tony Lindgren Signed-off-by: Grant Likely --- drivers/spi/omap2_mcspi.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index 2a651e61bfbf..df85990bb335 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -397,7 +397,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) if (tx != NULL) { wait_for_completion(&mcspi_dma->dma_tx_completion); - dma_unmap_single(NULL, xfer->tx_dma, count, DMA_TO_DEVICE); + dma_unmap_single(&spi->dev, xfer->tx_dma, count, DMA_TO_DEVICE); /* for TX_ONLY mode, be sure all words have shifted out */ if (rx == NULL) { @@ -412,7 +412,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) if (rx != NULL) { wait_for_completion(&mcspi_dma->dma_rx_completion); - dma_unmap_single(NULL, xfer->rx_dma, count, DMA_FROM_DEVICE); + dma_unmap_single(&spi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE); omap2_mcspi_set_enable(spi, 0); if (l & OMAP2_MCSPI_CHCONF_TURBO) { @@ -1025,11 +1025,6 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) if (m->is_dma_mapped || len < DMA_MIN_BYTES) continue; - /* Do DMA mapping "early" for better error reporting and - * dcache use. Note that if dma_unmap_single() ever starts - * to do real work on ARM, we'd need to clean up mappings - * for previous transfers on *ALL* exits of this loop... - */ if (tx_buf != NULL) { t->tx_dma = dma_map_single(&spi->dev, (void *) tx_buf, len, DMA_TO_DEVICE); @@ -1046,7 +1041,7 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) dev_dbg(&spi->dev, "dma %cX %d bytes error\n", 'R', len); if (tx_buf != NULL) - dma_unmap_single(NULL, t->tx_dma, + dma_unmap_single(&spi->dev, t->tx_dma, len, DMA_TO_DEVICE); return -EINVAL; } -- cgit v1.2.2 From 76d9cc454a8d0bb7484616a4b8136280068c8a8b Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Mon, 10 Jan 2011 11:05:06 +0000 Subject: spi: tegra: don't treat NULL clk as an error Some platforms have been known to return NULL from clk_get() if they support only a single struct clk. Whilst tegra doesn't do this, make the drivers consistent with others. Signed-off-by: Jamie Iles Acked-by: Russell King Signed-off-by: Grant Likely --- drivers/spi/spi_tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_tegra.c b/drivers/spi/spi_tegra.c index bb7df02a5472..891e5909038c 100644 --- a/drivers/spi/spi_tegra.c +++ b/drivers/spi/spi_tegra.c @@ -513,7 +513,7 @@ static int __init spi_tegra_probe(struct platform_device *pdev) } tspi->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR_OR_NULL(tspi->clk)) { + if (IS_ERR(tspi->clk)) { dev_err(&pdev->dev, "can not get clock\n"); ret = PTR_ERR(tspi->clk); goto err2; -- cgit v1.2.2 From 50c01fc355c6a97c511d58411f9bc0e4b8fc4659 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Tue, 11 Jan 2011 12:43:52 +0000 Subject: spi/dw_spi: don't treat NULL clk as an error clk_get() returns a struct clk cookie to the driver and some platforms may return NULL if they only support a single clock. clk_get() has only failed if it returns a ERR_PTR() encoded pointer. Signed-off-by: Jamie Iles Signed-off-by: Grant Likely --- drivers/spi/dw_spi_mmio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/dw_spi_mmio.c b/drivers/spi/dw_spi_mmio.c index db35bd9c1b24..2fa012c109bc 100644 --- a/drivers/spi/dw_spi_mmio.c +++ b/drivers/spi/dw_spi_mmio.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -68,8 +69,8 @@ static int __devinit dw_spi_mmio_probe(struct platform_device *pdev) } dwsmmio->clk = clk_get(&pdev->dev, NULL); - if (!dwsmmio->clk) { - ret = -ENODEV; + if (IS_ERR(dwsmmio->clk)) { + ret = PTR_ERR(dwsmmio->clk); goto err_irq; } clk_enable(dwsmmio->clk); -- cgit v1.2.2 From 77e7bc6194655c36d43707ea53b4f86d1f1c8cf5 Mon Sep 17 00:00:00 2001 From: Yong Shen Date: Tue, 11 Jan 2011 17:21:53 +0800 Subject: spi/imx: Add i.MX53 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Change the Kconfig to include i.MX53 2. add devtype entry for i.MX53 Signed-off-by: Yong Shen Acked-by: Sascha Hauer Acked-by: Uwe Kleine-König Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 4 ++-- drivers/spi/spi_imx.c | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 1906840c1113..879b2a974d64 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -156,10 +156,10 @@ config SPI_IMX_VER_0_4 def_bool y if ARCH_MX31 config SPI_IMX_VER_0_7 - def_bool y if ARCH_MX25 || ARCH_MX35 || ARCH_MX51 + def_bool y if ARCH_MX25 || ARCH_MX35 || ARCH_MX51 || ARCH_MX53 config SPI_IMX_VER_2_3 - def_bool y if ARCH_MX51 + def_bool y if ARCH_MX51 || ARCH_MX53 config SPI_IMX tristate "Freescale i.MX SPI controllers" diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 9469564e6888..1cf9d5faabf4 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -742,6 +742,12 @@ static struct platform_device_id spi_imx_devtype[] = { }, { .name = "imx51-ecspi", .driver_data = SPI_IMX_VER_2_3, + }, { + .name = "imx53-cspi", + .driver_data = SPI_IMX_VER_0_7, + }, { + .name = "imx53-ecspi", + .driver_data = SPI_IMX_VER_2_3, }, { /* sentinel */ } -- cgit v1.2.2 From 5f35765d836befebdfabf745fdbf2e070c887fac Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Wed, 12 Jan 2011 15:00:23 +0900 Subject: spi: Enable SPI driver for S5P6440 and S5P6450 This patch enables the existing S3C64XX series SPI driver for S5P64X0 and removed dependency on EXPERIMENTAL because we don't need it now. v3: Changed dependency of S3C64XX_DMA v2: Removed dependency on EXPERIMENTAL Signed-off-by: Abhilash Kesavan Signed-off-by: Sangbeom Kim Acked-by: Jassi Brar Signed-off-by: Kukjin Kim Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 879b2a974d64..13bfa9d48082 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -310,8 +310,8 @@ config SPI_S3C24XX_GPIO config SPI_S3C64XX tristate "Samsung S3C64XX series type SPI" - depends on ARCH_S3C64XX && EXPERIMENTAL - select S3C64XX_DMA + depends on (ARCH_S3C64XX || ARCH_S5P64X0) + select S3C64XX_DMA if ARCH_S3C64XX help SPI driver for Samsung S3C64XX and newer SoCs. -- cgit v1.2.2 From 94a1b6d8eb903818773ad24fe48529b0d295052d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Jan 2011 17:24:22 +0530 Subject: spi/amba-pl022: fixing compilation warning. clk_freq is used uninitialized in pl022_setup routine. This patch fix compilation warning for using uninitialized variable Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index a2a5921c730a..71a1219a995d 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1795,7 +1795,7 @@ static int pl022_setup(struct spi_device *spi) { struct pl022_config_chip const *chip_info; struct chip_data *chip; - struct ssp_clock_params clk_freq; + struct ssp_clock_params clk_freq = {0, }; int status = 0; struct pl022 *pl022 = spi_master_get_devdata(spi->master); unsigned int bits = spi->bits_per_word; -- cgit v1.2.2 From d95defaca7994ffff1ceb52f3011eb644dfe3274 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 17 Jan 2011 17:01:07 +0100 Subject: spi/spi_sh_msiof: fix a wrong free_irq() parameter Without this fix reloading of the driver is impossible. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Grant Likely --- drivers/spi/spi_sh_msiof.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_sh_msiof.c b/drivers/spi/spi_sh_msiof.c index d93b66743ba7..56f60c8ea0ab 100644 --- a/drivers/spi/spi_sh_msiof.c +++ b/drivers/spi/spi_sh_msiof.c @@ -635,7 +635,7 @@ static int sh_msiof_spi_remove(struct platform_device *pdev) ret = spi_bitbang_stop(&p->bitbang); if (!ret) { pm_runtime_disable(&pdev->dev); - free_irq(platform_get_irq(pdev, 0), sh_msiof_spi_irq); + free_irq(platform_get_irq(pdev, 0), p); iounmap(p->mapbase); clk_put(p->clk); spi_master_put(p->bitbang.master); -- cgit v1.2.2 From 8efaef4dc842a8a050d10aef30e26220b8995fc3 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jan 2011 21:28:22 +0100 Subject: SPI: Add SPI controller driver for the Atheros AR71XX/AR724X/AR913X SoCs The Atheros AR71XX/AR724X/AR913X SoCs have a built-in SPI controller. This patch implements a driver for that. Signed-off-by: Gabor Juhos Cc: David Brownell Cc: spi-devel-general@lists.sourceforge.net Acked-by: Grant Likely Cc: linux-mips@linux-mips.org Cc: Imre Kaloz Cc: Luis R. Rodriguez Cc: Cliff Holden Cc: Kathy Giori Patchwork: https://patchwork.linux-mips.org/patch/1960/ Signed-off-by: Ralf Baechle --- drivers/spi/Kconfig | 8 ++ drivers/spi/Makefile | 1 + drivers/spi/ath79_spi.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 301 insertions(+) create mode 100644 drivers/spi/ath79_spi.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 13bfa9d48082..bb233a9cbad2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -53,6 +53,14 @@ if SPI_MASTER comment "SPI Master Controller Drivers" +config SPI_ATH79 + tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver" + depends on ATH79 && GENERIC_GPIO + select SPI_BITBANG + help + This enables support for the SPI controller present on the + Atheros AR71XX/AR724X/AR913X SoCs. + config SPI_ATMEL tristate "Atmel SPI Controller" depends on (ARCH_AT91 || AVR32) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3a42463c92a4..86d1b5f9bbd9 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_SPI_MASTER) += spi.o # SPI master controller drivers (bus) obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o +obj-$(CONFIG_SPI_ATH79) += ath79_spi.o obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_AU1550) += au1550_spi.o diff --git a/drivers/spi/ath79_spi.c b/drivers/spi/ath79_spi.c new file mode 100644 index 000000000000..fcff810ea3b0 --- /dev/null +++ b/drivers/spi/ath79_spi.c @@ -0,0 +1,292 @@ +/* + * SPI controller driver for the Atheros AR71XX/AR724X/AR913X SoCs + * + * Copyright (C) 2009-2011 Gabor Juhos + * + * This driver has been based on the spi-gpio.c: + * Copyright (C) 2006,2008 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DRV_NAME "ath79-spi" + +struct ath79_spi { + struct spi_bitbang bitbang; + u32 ioc_base; + u32 reg_ctrl; + void __iomem *base; +}; + +static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned reg) +{ + return ioread32(sp->base + reg); +} + +static inline void ath79_spi_wr(struct ath79_spi *sp, unsigned reg, u32 val) +{ + iowrite32(val, sp->base + reg); +} + +static inline struct ath79_spi *ath79_spidev_to_sp(struct spi_device *spi) +{ + return spi_master_get_devdata(spi->master); +} + +static void ath79_spi_chipselect(struct spi_device *spi, int is_active) +{ + struct ath79_spi *sp = ath79_spidev_to_sp(spi); + int cs_high = (spi->mode & SPI_CS_HIGH) ? is_active : !is_active; + + if (is_active) { + /* set initial clock polarity */ + if (spi->mode & SPI_CPOL) + sp->ioc_base |= AR71XX_SPI_IOC_CLK; + else + sp->ioc_base &= ~AR71XX_SPI_IOC_CLK; + + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); + } + + if (spi->chip_select) { + struct ath79_spi_controller_data *cdata = spi->controller_data; + + /* SPI is normally active-low */ + gpio_set_value(cdata->gpio, cs_high); + } else { + if (cs_high) + sp->ioc_base |= AR71XX_SPI_IOC_CS0; + else + sp->ioc_base &= ~AR71XX_SPI_IOC_CS0; + + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); + } + +} + +static int ath79_spi_setup_cs(struct spi_device *spi) +{ + struct ath79_spi *sp = ath79_spidev_to_sp(spi); + struct ath79_spi_controller_data *cdata; + + cdata = spi->controller_data; + if (spi->chip_select && !cdata) + return -EINVAL; + + /* enable GPIO mode */ + ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); + + /* save CTRL register */ + sp->reg_ctrl = ath79_spi_rr(sp, AR71XX_SPI_REG_CTRL); + sp->ioc_base = ath79_spi_rr(sp, AR71XX_SPI_REG_IOC); + + /* TODO: setup speed? */ + ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, 0x43); + + if (spi->chip_select) { + int status = 0; + + status = gpio_request(cdata->gpio, dev_name(&spi->dev)); + if (status) + return status; + + status = gpio_direction_output(cdata->gpio, + spi->mode & SPI_CS_HIGH); + if (status) { + gpio_free(cdata->gpio); + return status; + } + } else { + if (spi->mode & SPI_CS_HIGH) + sp->ioc_base |= AR71XX_SPI_IOC_CS0; + else + sp->ioc_base &= ~AR71XX_SPI_IOC_CS0; + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); + } + + return 0; +} + +static void ath79_spi_cleanup_cs(struct spi_device *spi) +{ + struct ath79_spi *sp = ath79_spidev_to_sp(spi); + + if (spi->chip_select) { + struct ath79_spi_controller_data *cdata = spi->controller_data; + gpio_free(cdata->gpio); + } + + /* restore CTRL register */ + ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, sp->reg_ctrl); + /* disable GPIO mode */ + ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0); +} + +static int ath79_spi_setup(struct spi_device *spi) +{ + int status = 0; + + if (spi->bits_per_word > 32) + return -EINVAL; + + if (!spi->controller_state) { + status = ath79_spi_setup_cs(spi); + if (status) + return status; + } + + status = spi_bitbang_setup(spi); + if (status && !spi->controller_state) + ath79_spi_cleanup_cs(spi); + + return status; +} + +static void ath79_spi_cleanup(struct spi_device *spi) +{ + ath79_spi_cleanup_cs(spi); + spi_bitbang_cleanup(spi); +} + +static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs, + u32 word, u8 bits) +{ + struct ath79_spi *sp = ath79_spidev_to_sp(spi); + u32 ioc = sp->ioc_base; + + /* clock starts at inactive polarity */ + for (word <<= (32 - bits); likely(bits); bits--) { + u32 out; + + if (word & (1 << 31)) + out = ioc | AR71XX_SPI_IOC_DO; + else + out = ioc & ~AR71XX_SPI_IOC_DO; + + /* setup MSB (to slave) on trailing edge */ + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out); + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out | AR71XX_SPI_IOC_CLK); + + word <<= 1; + } + + return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS); +} + +static __devinit int ath79_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct ath79_spi *sp; + struct ath79_spi_platform_data *pdata; + struct resource *r; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof(*sp)); + if (master == NULL) { + dev_err(&pdev->dev, "failed to allocate spi master\n"); + return -ENOMEM; + } + + sp = spi_master_get_devdata(master); + platform_set_drvdata(pdev, sp); + + pdata = pdev->dev.platform_data; + + master->setup = ath79_spi_setup; + master->cleanup = ath79_spi_cleanup; + if (pdata) { + master->bus_num = pdata->bus_num; + master->num_chipselect = pdata->num_chipselect; + } else { + master->bus_num = -1; + master->num_chipselect = 1; + } + + sp->bitbang.master = spi_master_get(master); + sp->bitbang.chipselect = ath79_spi_chipselect; + sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0; + sp->bitbang.setup_transfer = spi_bitbang_setup_transfer; + sp->bitbang.flags = SPI_CS_HIGH; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENOENT; + goto err_put_master; + } + + sp->base = ioremap(r->start, r->end - r->start + 1); + if (!sp->base) { + ret = -ENXIO; + goto err_put_master; + } + + ret = spi_bitbang_start(&sp->bitbang); + if (ret) + goto err_unmap; + + return 0; + +err_unmap: + iounmap(sp->base); +err_put_master: + platform_set_drvdata(pdev, NULL); + spi_master_put(sp->bitbang.master); + + return ret; +} + +static __devexit int ath79_spi_remove(struct platform_device *pdev) +{ + struct ath79_spi *sp = platform_get_drvdata(pdev); + + spi_bitbang_stop(&sp->bitbang); + iounmap(sp->base); + platform_set_drvdata(pdev, NULL); + spi_master_put(sp->bitbang.master); + + return 0; +} + +static struct platform_driver ath79_spi_driver = { + .probe = ath79_spi_probe, + .remove = __devexit_p(ath79_spi_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static __init int ath79_spi_init(void) +{ + return platform_driver_register(&ath79_spi_driver); +} +module_init(ath79_spi_init); + +static __exit void ath79_spi_exit(void) +{ + platform_driver_unregister(&ath79_spi_driver); +} +module_exit(ath79_spi_exit); + +MODULE_DESCRIPTION("SPI controller driver for Atheros AR71XX/AR724X/AR913X"); +MODULE_AUTHOR("Gabor Juhos "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.2.2 From c9e358dfc4a8cb2227172ef77908c2e0ee17bcb9 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 21 Jan 2011 09:24:48 -0700 Subject: driver-core: remove conditionals around devicetree pointers Having conditional around the of_match_table and the of_node pointers turns out to make driver code use ugly #ifdef blocks. Drop the conditionals and remove the #ifdef blocks from the affected drivers. Also tidy up minor whitespace issues within the same hunks. Signed-off-by: Grant Likely Acked-by: Greg Kroah-Hartman --- drivers/spi/pxa2xx_spi.c | 2 -- drivers/spi/pxa2xx_spi_pci.c | 2 -- drivers/spi/xilinx_spi.c | 6 ------ 3 files changed, 10 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 95928833855b..a429b01d0285 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1557,9 +1557,7 @@ static int __devinit pxa2xx_spi_probe(struct platform_device *pdev) drv_data->ssp = ssp; master->dev.parent = &pdev->dev; -#ifdef CONFIG_OF master->dev.of_node = pdev->dev.of_node; -#endif /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; diff --git a/drivers/spi/pxa2xx_spi_pci.c b/drivers/spi/pxa2xx_spi_pci.c index 351d8a375b57..b6589bb3a6c3 100644 --- a/drivers/spi/pxa2xx_spi_pci.c +++ b/drivers/spi/pxa2xx_spi_pci.c @@ -98,9 +98,7 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev, pdev->dev.parent = &dev->dev; pdev->dev.platform_data = &spi_info->spi_pdata; -#ifdef CONFIG_OF pdev->dev.of_node = dev->dev.of_node; -#endif pdev->dev.release = plat_dev_release; spi_pdata->num_chipselect = dev->devfn; diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 7adaef62a991..4d2c75df886c 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -351,14 +351,12 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_OF static const struct of_device_id xilinx_spi_of_match[] = { { .compatible = "xlnx,xps-spi-2.00.a", }, { .compatible = "xlnx,xps-spi-2.00.b", }, {} }; MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); -#endif struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, u32 irq, s16 bus_num, int num_cs, int little_endian, int bits_per_word) @@ -394,9 +392,7 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, master->bus_num = bus_num; master->num_chipselect = num_cs; -#ifdef CONFIG_OF master->dev.of_node = dev->of_node; -#endif xspi->mem = *mem; xspi->irq = irq; @@ -539,9 +535,7 @@ static struct platform_driver xilinx_spi_driver = { .driver = { .name = XILINX_SPI_NAME, .owner = THIS_MODULE, -#ifdef CONFIG_OF .of_match_table = xilinx_spi_of_match, -#endif }, }; -- cgit v1.2.2 From 8a6afb9a950de01457a4267bcbe3292e56412326 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 21 Jan 2011 16:56:47 +0100 Subject: spi/spi_sh_msiof: fix wrong address calculation, which leads to an Oops NULL + != NULL, but reading from that address is usually not a very good idea and often leads to problems, like kernel Oopses in this case, easily reproducible by writing to an SD-card, used in SPI mode. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Grant Likely --- drivers/spi/spi_sh_msiof.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_sh_msiof.c b/drivers/spi/spi_sh_msiof.c index 56f60c8ea0ab..2c665fceaac7 100644 --- a/drivers/spi/spi_sh_msiof.c +++ b/drivers/spi/spi_sh_msiof.c @@ -509,9 +509,11 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t) bytes_done = 0; while (bytes_done < t->len) { + void *rx_buf = t->rx_buf ? t->rx_buf + bytes_done : NULL; + const void *tx_buf = t->tx_buf ? t->tx_buf + bytes_done : NULL; n = sh_msiof_spi_txrx_once(p, tx_fifo, rx_fifo, - t->tx_buf + bytes_done, - t->rx_buf + bytes_done, + tx_buf, + rx_buf, words, bits); if (n < 0) break; -- cgit v1.2.2 From e2dbf5ebcc983b0349ab507bf7dd5562cf88dd24 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 21 Jan 2011 16:56:37 +0100 Subject: spi/spi_sh_msiof: cosmetic clean-up 1. sort headers alphabetically 2. use fixed-size types u8, u16, u32 for register values and transferred data 3. simplify some arithmetic operations Signed-off-by: Guennadi Liakhovetski [grant.likely@secretlab.ca: removed label indentation change] Signed-off-by: Grant Likely --- drivers/spi/spi_sh_msiof.c | 68 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 34 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_sh_msiof.c b/drivers/spi/spi_sh_msiof.c index 56f60c8ea0ab..d220cb287874 100644 --- a/drivers/spi/spi_sh_msiof.c +++ b/drivers/spi/spi_sh_msiof.c @@ -9,22 +9,22 @@ * */ -#include -#include +#include +#include +#include #include +#include +#include +#include #include +#include +#include #include -#include #include -#include -#include -#include -#include -#include +#include #include #include -#include #include @@ -67,7 +67,7 @@ struct sh_msiof_spi_priv { #define STR_TEOF (1 << 23) #define STR_REOF (1 << 7) -static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs) +static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs) { switch (reg_offs) { case TSCR: @@ -79,7 +79,7 @@ static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs) } static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs, - unsigned long value) + u32 value) { switch (reg_offs) { case TSCR: @@ -93,10 +93,10 @@ static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs, } static int sh_msiof_modify_ctr_wait(struct sh_msiof_spi_priv *p, - unsigned long clr, unsigned long set) + u32 clr, u32 set) { - unsigned long mask = clr | set; - unsigned long data; + u32 mask = clr | set; + u32 data; int k; data = sh_msiof_read(p, CTR); @@ -166,10 +166,10 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, } static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, - int cpol, int cpha, - int tx_hi_z, int lsb_first) + u32 cpol, u32 cpha, + u32 tx_hi_z, u32 lsb_first) { - unsigned long tmp; + u32 tmp; int edge; /* @@ -187,7 +187,7 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, tmp |= cpol << 30; /* TSCKIZ */ tmp |= cpol << 28; /* RSCKIZ */ - edge = cpol ? cpha : !cpha; + edge = cpol ^ !cpha; tmp |= edge << 27; /* TEDG */ tmp |= edge << 26; /* REDG */ @@ -197,11 +197,9 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p, const void *tx_buf, void *rx_buf, - int bits, int words) + u32 bits, u32 words) { - unsigned long dr2; - - dr2 = ((bits - 1) << 24) | ((words - 1) << 16); + u32 dr2 = ((bits - 1) << 24) | ((words - 1) << 16); if (tx_buf) sh_msiof_write(p, TMDR2, dr2); @@ -222,7 +220,7 @@ static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p) static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p, const void *tx_buf, int words, int fs) { - const unsigned char *buf_8 = tx_buf; + const u8 *buf_8 = tx_buf; int k; for (k = 0; k < words; k++) @@ -232,7 +230,7 @@ static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p, const void *tx_buf, int words, int fs) { - const unsigned short *buf_16 = tx_buf; + const u16 *buf_16 = tx_buf; int k; for (k = 0; k < words; k++) @@ -242,7 +240,7 @@ static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p, const void *tx_buf, int words, int fs) { - const unsigned short *buf_16 = tx_buf; + const u16 *buf_16 = tx_buf; int k; for (k = 0; k < words; k++) @@ -252,7 +250,7 @@ static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p, const void *tx_buf, int words, int fs) { - const unsigned int *buf_32 = tx_buf; + const u32 *buf_32 = tx_buf; int k; for (k = 0; k < words; k++) @@ -262,7 +260,7 @@ static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p, const void *tx_buf, int words, int fs) { - const unsigned int *buf_32 = tx_buf; + const u32 *buf_32 = tx_buf; int k; for (k = 0; k < words; k++) @@ -272,7 +270,7 @@ static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p, void *rx_buf, int words, int fs) { - unsigned char *buf_8 = rx_buf; + u8 *buf_8 = rx_buf; int k; for (k = 0; k < words; k++) @@ -282,7 +280,7 @@ static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p, void *rx_buf, int words, int fs) { - unsigned short *buf_16 = rx_buf; + u16 *buf_16 = rx_buf; int k; for (k = 0; k < words; k++) @@ -292,7 +290,7 @@ static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p, void *rx_buf, int words, int fs) { - unsigned short *buf_16 = rx_buf; + u16 *buf_16 = rx_buf; int k; for (k = 0; k < words; k++) @@ -302,7 +300,7 @@ static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p, void *rx_buf, int words, int fs) { - unsigned int *buf_32 = rx_buf; + u32 *buf_32 = rx_buf; int k; for (k = 0; k < words; k++) @@ -312,7 +310,7 @@ static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_read_fifo_32u(struct sh_msiof_spi_priv *p, void *rx_buf, int words, int fs) { - unsigned int *buf_32 = rx_buf; + u32 *buf_32 = rx_buf; int k; for (k = 0; k < words; k++) @@ -324,7 +322,8 @@ static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t) int bits; bits = t ? t->bits_per_word : 0; - bits = bits ? bits : spi->bits_per_word; + if (!bits) + bits = spi->bits_per_word; return bits; } @@ -334,7 +333,8 @@ static unsigned long sh_msiof_spi_hz(struct spi_device *spi, unsigned long hz; hz = t ? t->speed_hz : 0; - hz = hz ? hz : spi->max_speed_hz; + if (!hz) + hz = spi->max_speed_hz; return hz; } -- cgit v1.2.2 From 9dabb3f3269d042908bf1f4e685413c39cc8c373 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 21 Jan 2011 16:56:42 +0100 Subject: spi/spi_sh_msiof: consolidate data in 8-bit mode into 32-bit words Instead of sending data 8 bits at a time in 8-bit SPI mode, swap bytes and send and receive them 32 bits at a time. Tested with an SD-card, with which this patch reduced the number of interrupts by 50%, when reading 5MiB of data (there are also small service packets, the number of interrupts, produced by 512-byte sectors should, of course, drop by 75%), and improved throughput by more than 40%. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Grant Likely --- drivers/spi/spi_sh_msiof.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_sh_msiof.c b/drivers/spi/spi_sh_msiof.c index d220cb287874..da6e42ec856e 100644 --- a/drivers/spi/spi_sh_msiof.c +++ b/drivers/spi/spi_sh_msiof.c @@ -267,6 +267,26 @@ static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p, sh_msiof_write(p, TFDR, get_unaligned(&buf_32[k]) << fs); } +static void sh_msiof_spi_write_fifo_s32(struct sh_msiof_spi_priv *p, + const void *tx_buf, int words, int fs) +{ + const u32 *buf_32 = tx_buf; + int k; + + for (k = 0; k < words; k++) + sh_msiof_write(p, TFDR, swab32(buf_32[k] << fs)); +} + +static void sh_msiof_spi_write_fifo_s32u(struct sh_msiof_spi_priv *p, + const void *tx_buf, int words, int fs) +{ + const u32 *buf_32 = tx_buf; + int k; + + for (k = 0; k < words; k++) + sh_msiof_write(p, TFDR, swab32(get_unaligned(&buf_32[k]) << fs)); +} + static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p, void *rx_buf, int words, int fs) { @@ -317,6 +337,26 @@ static void sh_msiof_spi_read_fifo_32u(struct sh_msiof_spi_priv *p, put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_32[k]); } +static void sh_msiof_spi_read_fifo_s32(struct sh_msiof_spi_priv *p, + void *rx_buf, int words, int fs) +{ + u32 *buf_32 = rx_buf; + int k; + + for (k = 0; k < words; k++) + buf_32[k] = swab32(sh_msiof_read(p, RFDR) >> fs); +} + +static void sh_msiof_spi_read_fifo_s32u(struct sh_msiof_spi_priv *p, + void *rx_buf, int words, int fs) +{ + u32 *buf_32 = rx_buf; + int k; + + for (k = 0; k < words; k++) + put_unaligned(swab32(sh_msiof_read(p, RFDR) >> fs), &buf_32[k]); +} + static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t) { int bits; @@ -468,9 +508,17 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t) int bytes_done; int words; int n; + bool swab; bits = sh_msiof_spi_bits(spi, t); + if (bits <= 8 && t->len > 15 && !(t->len & 3)) { + bits = 32; + swab = true; + } else { + swab = false; + } + /* setup bytes per word and fifo read/write functions */ if (bits <= 8) { bytes_per_word = 1; @@ -487,6 +535,17 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t) rx_fifo = sh_msiof_spi_read_fifo_16u; else rx_fifo = sh_msiof_spi_read_fifo_16; + } else if (swab) { + bytes_per_word = 4; + if ((unsigned long)t->tx_buf & 0x03) + tx_fifo = sh_msiof_spi_write_fifo_s32u; + else + tx_fifo = sh_msiof_spi_write_fifo_s32; + + if ((unsigned long)t->rx_buf & 0x03) + rx_fifo = sh_msiof_spi_read_fifo_s32u; + else + rx_fifo = sh_msiof_spi_read_fifo_s32; } else { bytes_per_word = 4; if ((unsigned long)t->tx_buf & 0x03) -- cgit v1.2.2 From 59b479e0985f0b795d68331d6443a7f89c47768d Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 27 Jan 2011 16:39:40 -0800 Subject: omap: Start using CONFIG_SOC_OMAP We want to have just CONFIG_ARCH_OMAP2, 3 and 4. The rest are nowadays just subcategories of these. Search and replace the following: ARCH_OMAP2420 SOC_OMAP2420 ARCH_OMAP2430 SOC_OMAP2430 ARCH_OMAP3430 SOC_OMAP3430 No functional changes. Signed-off-by: Tony Lindgren Signed-off-by: Thomas Weber Acked-by: Sourav Poddar --- drivers/spi/omap2_mcspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index abb1ffbf3d20..f076cc5c6fb0 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -1111,7 +1111,7 @@ static u8 __initdata spi2_txdma_id[] = { OMAP24XX_DMA_SPI2_TX1, }; -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \ +#if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \ || defined(CONFIG_ARCH_OMAP4) static u8 __initdata spi3_rxdma_id[] = { OMAP24XX_DMA_SPI3_RX0, @@ -1154,7 +1154,7 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) txdma_id = spi2_txdma_id; num_chipselect = 2; break; -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \ +#if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \ || defined(CONFIG_ARCH_OMAP4) case 3: rxdma_id = spi3_rxdma_id; -- cgit v1.2.2 From 7d48ec3698e7b747efa744fd340b0f2d1dbfd3e0 Mon Sep 17 00:00:00 2001 From: Bernhard Walle Date: Thu, 3 Feb 2011 09:37:18 +0100 Subject: spi/spidev: Add 32 bit compat ioctl() Add the compat_ioctl for operations on /dev/spi* so that 32 bit userspace applications can access SPI. As far as I can see all data structure are already prepared for that, so no additional conversion has to be done. My use case is MIPS with N32 userspace ABI and toolchain, and that was also the platform where I tested it successfully (Cavium Octeon). Signed-off-by: Bernhard Walle Reviewed-by: Arnd Bergmann Signed-off-by: Grant Likely --- drivers/spi/spidev.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 603428213d21..d9fd86211365 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -471,6 +472,16 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return retval; } +#ifdef CONFIG_COMPAT +static long +spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#else +#define spidev_compat_ioctl NULL +#endif /* CONFIG_COMPAT */ + static int spidev_open(struct inode *inode, struct file *filp) { struct spidev_data *spidev; @@ -543,6 +554,7 @@ static const struct file_operations spidev_fops = { .write = spidev_write, .read = spidev_read, .unlocked_ioctl = spidev_ioctl, + .compat_ioctl = spidev_compat_ioctl, .open = spidev_open, .release = spidev_release, .llseek = no_llseek, -- cgit v1.2.2 From 0f3e1d27a7e3f98d996d707d649128e229b65deb Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 3 Feb 2011 00:31:21 +0530 Subject: spi/pxa2xx pci: fix the release - remove race Right now the platform device and its platform data is included in one big struct which requires its custom ->release function. The problem with the release function within the driver is that it might be called after the driver was removed because someone was holding a reference to it and it was not called right after platform_device_unregister(). So we also free the platform device memory to which one might hold a reference. This patch uses the normal pdev functions so this kind of race does not occur. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Grant Likely --- drivers/spi/pxa2xx_spi_pci.c | 61 +++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 40 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/pxa2xx_spi_pci.c b/drivers/spi/pxa2xx_spi_pci.c index 351d8a375b57..19752b09e155 100644 --- a/drivers/spi/pxa2xx_spi_pci.c +++ b/drivers/spi/pxa2xx_spi_pci.c @@ -7,10 +7,9 @@ #include #include -struct awesome_struct { +struct ce4100_info { struct ssp_device ssp; - struct platform_device spi_pdev; - struct pxa2xx_spi_master spi_pdata; + struct platform_device *spi_pdev; }; static DEFINE_MUTEX(ssp_lock); @@ -51,23 +50,15 @@ void pxa_ssp_free(struct ssp_device *ssp) } EXPORT_SYMBOL_GPL(pxa_ssp_free); -static void plat_dev_release(struct device *dev) -{ - struct awesome_struct *as = container_of(dev, - struct awesome_struct, spi_pdev.dev); - - of_device_node_put(&as->spi_pdev.dev); -} - static int __devinit ce4100_spi_probe(struct pci_dev *dev, const struct pci_device_id *ent) { int ret; resource_size_t phys_beg; resource_size_t phys_len; - struct awesome_struct *spi_info; + struct ce4100_info *spi_info; struct platform_device *pdev; - struct pxa2xx_spi_master *spi_pdata; + struct pxa2xx_spi_master spi_pdata; struct ssp_device *ssp; ret = pci_enable_device(dev); @@ -84,33 +75,30 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev, return ret; } + pdev = platform_device_alloc("pxa2xx-spi", dev->devfn); spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL); - if (!spi_info) { + if (!pdev || !spi_info ) { ret = -ENOMEM; - goto err_kz; + goto err_nomem; } - ssp = &spi_info->ssp; - pdev = &spi_info->spi_pdev; - spi_pdata = &spi_info->spi_pdata; + memset(&spi_pdata, 0, sizeof(spi_pdata)); + spi_pdata.num_chipselect = dev->devfn; - pdev->name = "pxa2xx-spi"; - pdev->id = dev->devfn; - pdev->dev.parent = &dev->dev; - pdev->dev.platform_data = &spi_info->spi_pdata; + ret = platform_device_add_data(pdev, &spi_pdata, sizeof(spi_pdata)); + if (ret) + goto err_nomem; + pdev->dev.parent = &dev->dev; #ifdef CONFIG_OF pdev->dev.of_node = dev->dev.of_node; #endif - pdev->dev.release = plat_dev_release; - - spi_pdata->num_chipselect = dev->devfn; - + ssp = &spi_info->ssp; ssp->phys_base = pci_resource_start(dev, 0); ssp->mmio_base = ioremap(phys_beg, phys_len); if (!ssp->mmio_base) { dev_err(&pdev->dev, "failed to ioremap() registers\n"); ret = -EIO; - goto err_remap; + goto err_nomem; } ssp->irq = dev->irq; ssp->port_id = pdev->id; @@ -122,7 +110,7 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev, pci_set_drvdata(dev, spi_info); - ret = platform_device_register(pdev); + ret = platform_device_add(pdev); if (ret) goto err_dev_add; @@ -135,27 +123,21 @@ err_dev_add: mutex_unlock(&ssp_lock); iounmap(ssp->mmio_base); -err_remap: - kfree(spi_info); - -err_kz: +err_nomem: release_mem_region(phys_beg, phys_len); - + platform_device_put(pdev); + kfree(spi_info); return ret; } static void __devexit ce4100_spi_remove(struct pci_dev *dev) { - struct awesome_struct *spi_info; - struct platform_device *pdev; + struct ce4100_info *spi_info; struct ssp_device *ssp; spi_info = pci_get_drvdata(dev); - ssp = &spi_info->ssp; - pdev = &spi_info->spi_pdev; - - platform_device_unregister(pdev); + platform_device_unregister(spi_info->spi_pdev); iounmap(ssp->mmio_base); release_mem_region(pci_resource_start(dev, 0), @@ -171,7 +153,6 @@ static void __devexit ce4100_spi_remove(struct pci_dev *dev) } static struct pci_device_id ce4100_spi_devices[] __devinitdata = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) }, { }, }; -- cgit v1.2.2 From 4d676fc5c39a677aa72148debd47029d8d8f0634 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Tue, 11 Jan 2011 11:19:07 -0500 Subject: spi/bfin_spi: support for multiples of 8bits with hardware CS We can do multiples of 8bit transfers when using the hardware CS and a little bit of magic, so make it work. Signed-off-by: Bob Liu Signed-off-by: Mike Frysinger Signed-off-by: Grant Likely --- drivers/spi/spi_bfin5xx.c | 103 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 32 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 3f223511127b..e8d68b79e98f 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -425,6 +425,7 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) struct bfin_spi_slave_data *chip = drv_data->cur_chip; struct spi_message *msg = drv_data->cur_msg; int n_bytes = drv_data->n_bytes; + int loop = 0; /* wait until transfer finished. */ while (!(read_STAT(drv_data) & BIT_STAT_RXS)) @@ -435,10 +436,15 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) /* last read */ if (drv_data->rx) { dev_dbg(&drv_data->pdev->dev, "last read\n"); - if (n_bytes == 2) - *(u16 *) (drv_data->rx) = read_RDBR(drv_data); - else if (n_bytes == 1) - *(u8 *) (drv_data->rx) = read_RDBR(drv_data); + if (n_bytes % 2) { + u16 *buf = (u16 *)drv_data->rx; + for (loop = 0; loop < n_bytes / 2; loop++) + *buf++ = read_RDBR(drv_data); + } else { + u8 *buf = (u8 *)drv_data->rx; + for (loop = 0; loop < n_bytes; loop++) + *buf++ = read_RDBR(drv_data); + } drv_data->rx += n_bytes; } @@ -458,29 +464,53 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) if (drv_data->rx && drv_data->tx) { /* duplex */ dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n"); - if (drv_data->n_bytes == 2) { - *(u16 *) (drv_data->rx) = read_RDBR(drv_data); - write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); - } else if (drv_data->n_bytes == 1) { - *(u8 *) (drv_data->rx) = read_RDBR(drv_data); - write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); + if (n_bytes % 2) { + u16 *buf = (u16 *)drv_data->rx; + u16 *buf2 = (u16 *)drv_data->tx; + for (loop = 0; loop < n_bytes / 2; loop++) { + *buf++ = read_RDBR(drv_data); + write_TDBR(drv_data, *buf2++); + } + } else { + u8 *buf = (u8 *)drv_data->rx; + u8 *buf2 = (u8 *)drv_data->tx; + for (loop = 0; loop < n_bytes; loop++) { + *buf++ = read_RDBR(drv_data); + write_TDBR(drv_data, *buf2++); + } } } else if (drv_data->rx) { /* read */ dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n"); - if (drv_data->n_bytes == 2) - *(u16 *) (drv_data->rx) = read_RDBR(drv_data); - else if (drv_data->n_bytes == 1) - *(u8 *) (drv_data->rx) = read_RDBR(drv_data); - write_TDBR(drv_data, chip->idle_tx_val); + if (n_bytes % 2) { + u16 *buf = (u16 *)drv_data->rx; + for (loop = 0; loop < n_bytes / 2; loop++) { + *buf++ = read_RDBR(drv_data); + write_TDBR(drv_data, chip->idle_tx_val); + } + } else { + u8 *buf = (u8 *)drv_data->rx; + for (loop = 0; loop < n_bytes; loop++) { + *buf++ = read_RDBR(drv_data); + write_TDBR(drv_data, chip->idle_tx_val); + } + } } else if (drv_data->tx) { /* write */ dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n"); - bfin_spi_dummy_read(drv_data); - if (drv_data->n_bytes == 2) - write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); - else if (drv_data->n_bytes == 1) - write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); + if (n_bytes % 2) { + u16 *buf = (u16 *)drv_data->tx; + for (loop = 0; loop < n_bytes / 2; loop++) { + read_RDBR(drv_data); + write_TDBR(drv_data, *buf++); + } + } else { + u8 *buf = (u8 *)drv_data->tx; + for (loop = 0; loop < n_bytes; loop++) { + read_RDBR(drv_data); + write_TDBR(drv_data, *buf++); + } + } } if (drv_data->tx) @@ -651,16 +681,16 @@ static void bfin_spi_pump_transfers(unsigned long data) /* Bits per word setup */ bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word; - if (bits_per_word == 8) { - drv_data->n_bytes = 1; - drv_data->len = transfer->len; - cr_width = 0; - drv_data->ops = &bfin_bfin_spi_transfer_ops_u8; - } else if (bits_per_word == 16) { - drv_data->n_bytes = 2; + if ((bits_per_word > 0) && (bits_per_word % 16 == 0)) { + drv_data->n_bytes = bits_per_word/8; drv_data->len = (transfer->len) >> 1; cr_width = BIT_CTL_WORDSIZE; drv_data->ops = &bfin_bfin_spi_transfer_ops_u16; + } else if ((bits_per_word > 0) && (bits_per_word % 8 == 0)) { + drv_data->n_bytes = bits_per_word/8; + drv_data->len = transfer->len; + cr_width = 0; + drv_data->ops = &bfin_bfin_spi_transfer_ops_u8; } else { dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word\n"); message->status = -EINVAL; @@ -815,10 +845,19 @@ static void bfin_spi_pump_transfers(unsigned long data) if (drv_data->tx == NULL) write_TDBR(drv_data, chip->idle_tx_val); else { - if (bits_per_word == 8) - write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); - else - write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); + int loop; + if (bits_per_word % 16 == 0) { + u16 *buf = (u16 *)drv_data->tx; + for (loop = 0; loop < bits_per_word / 16; + loop++) { + write_TDBR(drv_data, *buf++); + } + } else if (bits_per_word % 8 == 0) { + u8 *buf = (u8 *)drv_data->tx; + for (loop = 0; loop < bits_per_word / 8; loop++) + write_TDBR(drv_data, *buf++); + } + drv_data->tx += drv_data->n_bytes; } @@ -1031,7 +1070,7 @@ static int bfin_spi_setup(struct spi_device *spi) chip->ctl_reg &= bfin_ctl_reg; } - if (spi->bits_per_word != 8 && spi->bits_per_word != 16) { + if (spi->bits_per_word % 8) { dev_err(&spi->dev, "%d bits_per_word is not supported\n", spi->bits_per_word); goto error; -- cgit v1.2.2 From 1974eba605557e934764cb83c8ceb0eca78f011a Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 11 Jan 2011 11:19:08 -0500 Subject: spi/bfin_spi: return immediately after skipping to next transfer If there is an error with setting up a transfer, we need to return immediately rather than trying to continue to process things. We already set up the error states for the caller at this point. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Grant Likely --- drivers/spi/spi_bfin5xx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index e8d68b79e98f..a28462486df8 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -653,6 +653,7 @@ static void bfin_spi_pump_transfers(unsigned long data) message->state = bfin_spi_next_transfer(drv_data); /* Schedule next transfer tasklet */ tasklet_schedule(&drv_data->pump_transfers); + return; } if (transfer->tx_buf != NULL) { -- cgit v1.2.2 From ea3065df7ddffe4669136619ad712783ccee22c6 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 8 Feb 2011 10:46:14 +0100 Subject: spi/bitbang: check for setup_transfer during initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setup_transfer is mandatory if spi_bitbang_transfer is used, so check for it during initialization and not each time during runtime. Signed-off-by: Sascha Hauer Signed-off-by: Uwe Kleine-König Signed-off-by: Grant Likely --- drivers/spi/spi_bitbang.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index 8b55724d5f39..14a63f6010d1 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -259,10 +259,6 @@ static void bitbang_work(struct work_struct *work) struct spi_bitbang *bitbang = container_of(work, struct spi_bitbang, work); unsigned long flags; - int (*setup_transfer)(struct spi_device *, - struct spi_transfer *); - - setup_transfer = bitbang->setup_transfer; spin_lock_irqsave(&bitbang->lock, flags); bitbang->busy = 1; @@ -300,11 +296,7 @@ static void bitbang_work(struct work_struct *work) /* init (-1) or override (1) transfer params */ if (do_setup != 0) { - if (!setup_transfer) { - status = -ENOPROTOOPT; - break; - } - status = setup_transfer(spi, t); + status = bitbang->setup_transfer(spi, t); if (status < 0) break; if (do_setup == -1) @@ -465,6 +457,9 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) } } else if (!bitbang->master->setup) return -EINVAL; + if (bitbang->master->transfer == spi_bitbang_transfer && + !bitbang->setup_transfer) + return -EINVAL; /* this task is the only thing to touch the SPI bits */ bitbang->busy = 0; -- cgit v1.2.2 From ecd442fd9e388a31d382a62fb43e851048c282e1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 8 Feb 2011 13:03:12 +0100 Subject: spi/pl022: use dmaengine helper macros This simplifies the DMA code a bit by using the dmaengine helpers. The cookie from descriptor submission can be ignored in this case as has been established in review of the MMCI/PL180 driver. Cc: Dan Williams Cc: Russell King Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 71a1219a995d..4220aad42840 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -917,7 +917,6 @@ static int configure_dma(struct pl022 *pl022) struct dma_chan *txchan = pl022->dma_tx_channel; struct dma_async_tx_descriptor *rxdesc; struct dma_async_tx_descriptor *txdesc; - dma_cookie_t cookie; /* Check that the channels are available */ if (!rxchan || !txchan) @@ -962,10 +961,8 @@ static int configure_dma(struct pl022 *pl022) tx_conf.dst_addr_width = rx_conf.src_addr_width; BUG_ON(rx_conf.src_addr_width != tx_conf.dst_addr_width); - rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG, - (unsigned long) &rx_conf); - txchan->device->device_control(txchan, DMA_SLAVE_CONFIG, - (unsigned long) &tx_conf); + dmaengine_slave_config(rxchan, &rx_conf); + dmaengine_slave_config(txchan, &tx_conf); /* Create sglists for the transfers */ pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1; @@ -1018,23 +1015,19 @@ static int configure_dma(struct pl022 *pl022) rxdesc->callback_param = pl022; /* Submit and fire RX and TX with TX last so we're ready to read! */ - cookie = rxdesc->tx_submit(rxdesc); - if (dma_submit_error(cookie)) - goto err_submit_rx; - cookie = txdesc->tx_submit(txdesc); - if (dma_submit_error(cookie)) - goto err_submit_tx; - rxchan->device->device_issue_pending(rxchan); - txchan->device->device_issue_pending(txchan); + dmaengine_submit(rxdesc); + dmaengine_submit(txdesc); + dma_async_issue_pending(rxchan); + dma_async_issue_pending(txchan); return 0; err_submit_tx: err_submit_rx: err_txdesc: - txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(txchan); err_rxdesc: - rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(rxchan); dma_unmap_sg(txchan->device->dev, pl022->sgt_tx.sgl, pl022->sgt_tx.nents, DMA_TO_DEVICE); err_tx_sgmap: @@ -1101,8 +1094,8 @@ static void terminate_dma(struct pl022 *pl022) struct dma_chan *rxchan = pl022->dma_rx_channel; struct dma_chan *txchan = pl022->dma_tx_channel; - rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0); - txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(rxchan); + dmaengine_terminate_all(txchan); unmap_free_dma_scatter(pl022); } -- cgit v1.2.2 From 808f1037ab3fbf75d25026ad8e175c57927cdb3a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 8 Feb 2011 13:03:32 +0100 Subject: spi/pl022: disable core voltage when idle This utilizes the new core voltage switch to power off the PL022 core voltage when it's not in use transmitting packets, if and only if a core voltage switch is available. Cc: Russell King Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 4220aad42840..8fdf530f9f50 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -508,9 +508,10 @@ static void giveback(struct pl022 *pl022) msg->state = NULL; if (msg->complete) msg->complete(msg->context); - /* This message is completed, so let's turn off the clocks! */ + /* This message is completed, so let's turn off the clocks & power */ clk_disable(pl022->clk); amba_pclk_disable(pl022->adev); + amba_vcore_disable(pl022->adev); } /** @@ -1475,9 +1476,11 @@ static void pump_messages(struct work_struct *work) /* Setup the SPI using the per chip configuration */ pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi); /* - * We enable the clocks here, then the clocks will be disabled when - * giveback() is called in each method (poll/interrupt/DMA) + * We enable the core voltage and clocks here, then the clocks + * and core will be disabled when giveback() is called in each method + * (poll/interrupt/DMA) */ + amba_vcore_enable(pl022->adev); amba_pclk_enable(pl022->adev); clk_enable(pl022->clk); restore_state(pl022); @@ -2123,8 +2126,12 @@ pl022_probe(struct amba_device *adev, struct amba_id *id) goto err_spi_register; } dev_dbg(dev, "probe succeded\n"); - /* Disable the silicon block pclk and clock it when needed */ + /* + * Disable the silicon block pclk and any voltage domain and just + * power it up and clock it when it's needed + */ amba_pclk_disable(adev); + amba_vcore_disable(adev); return 0; err_spi_register: @@ -2189,9 +2196,11 @@ static int pl022_suspend(struct amba_device *adev, pm_message_t state) return status; } + amba_vcore_enable(adev); amba_pclk_enable(adev); load_ssp_default_config(pl022); amba_pclk_disable(adev); + amba_vcore_disable(adev); dev_dbg(&adev->dev, "suspended\n"); return 0; } -- cgit v1.2.2 From d63636d34761e1146fc7d4ef896ca93c8073ef88 Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Tue, 8 Feb 2011 13:03:41 +0100 Subject: spi/pl022: remove dangling status check Signed-off-by: Virupax Sadashivpetimath Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 8fdf530f9f50..2b591f0b92b4 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1906,8 +1906,6 @@ static int pl022_setup(struct spi_device *spi) && ((pl022->master_info)->enable_dma)) { chip->enable_dma = true; dev_dbg(&spi->dev, "DMA mode set in controller state\n"); - if (status < 0) - goto err_config_params; SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED, SSP_DMACR_MASK_RXDMAE, 0); SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED, -- cgit v1.2.2 From 12e8b325f28a87ad006822d0561112c1751dfc9b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 8 Feb 2011 13:03:55 +0100 Subject: spi/pl022: minor kerneldoc updates Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 2b591f0b92b4..4cd05cc05a55 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -329,15 +329,16 @@ struct vendor_data { /** * struct pl022 - This is the private SSP driver data structure * @adev: AMBA device model hookup - * @vendor: Vendor data for the IP block - * @phybase: The physical memory where the SSP device resides - * @virtbase: The virtual memory where the SSP is mapped + * @vendor: vendor data for the IP block + * @phybase: the physical memory where the SSP device resides + * @virtbase: the virtual memory where the SSP is mapped + * @clk: outgoing clock "SPICLK" for the SPI bus * @master: SPI framework hookup * @master_info: controller-specific data from machine setup - * @regs: SSP controller register's virtual address - * @pump_messages: Work struct for scheduling work to the workqueue - * @lock: spinlock to syncronise access to driver data * @workqueue: a workqueue on which any spi_message request is queued + * @pump_messages: work struct for scheduling work to the workqueue + * @queue_lock: spinlock to syncronise access to message queue + * @queue: message queue * @busy: workqueue is busy * @running: workqueue is running * @pump_transfers: Tasklet used in Interrupt Transfer mode @@ -348,8 +349,14 @@ struct vendor_data { * @tx_end: end position in TX buffer to be read * @rx: current position in RX buffer to be written * @rx_end: end position in RX buffer to be written - * @readingtype: the type of read currently going on - * @writingtype: the type or write currently going on + * @read: the type of read currently going on + * @write: the type of write currently going on + * @exp_fifo_level: expected FIFO level + * @dma_rx_channel: optional channel for RX DMA + * @dma_tx_channel: optional channel for TX DMA + * @sgt_rx: scattertable for the RX transfer + * @sgt_tx: scattertable for the TX transfer + * @dummypage: a dummy page used for driving data on the bus with DMA */ struct pl022 { struct amba_device *adev; @@ -397,8 +404,8 @@ struct pl022 { * @cpsr: Value of Clock prescale register * @n_bytes: how many bytes(power of 2) reqd for a given data width of client * @enable_dma: Whether to enable DMA or not - * @write: function ptr to be used to write when doing xfer for this chip * @read: function ptr to be used to read when doing xfer for this chip + * @write: function ptr to be used to write when doing xfer for this chip * @cs_control: chip select callback provided by chip * @xfer_type: polling/interrupt/DMA * -- cgit v1.2.2 From f020c39e51b1ef8389d5cf38190d32f55ff9d556 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 8 Feb 2011 21:08:59 +0100 Subject: spi/imx: select master mode for all channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hardware seems to have a race condition when the inactive channels are in slave mode. We support master mode only, so we can just switch all channels to master mode. Signed-off-by: Sascha Hauer [ukleinek: add more verbose comment about the race] Signed-off-by: Uwe Kleine-König Signed-off-by: Grant Likely --- drivers/spi/spi_imx.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 1cf9d5faabf4..69d6dba67c19 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -174,7 +174,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin, #define SPI_IMX2_3_CTRL 0x08 #define SPI_IMX2_3_CTRL_ENABLE (1 << 0) #define SPI_IMX2_3_CTRL_XCH (1 << 2) -#define SPI_IMX2_3_CTRL_MODE(cs) (1 << ((cs) + 4)) +#define SPI_IMX2_3_CTRL_MODE_MASK (0xf << 4) #define SPI_IMX2_3_CTRL_POSTDIV_OFFSET 8 #define SPI_IMX2_3_CTRL_PREDIV_OFFSET 12 #define SPI_IMX2_3_CTRL_CS(cs) ((cs) << 18) @@ -253,8 +253,14 @@ static int __maybe_unused spi_imx2_3_config(struct spi_imx_data *spi_imx, { u32 ctrl = SPI_IMX2_3_CTRL_ENABLE, cfg = 0; - /* set master mode */ - ctrl |= SPI_IMX2_3_CTRL_MODE(config->cs); + /* + * The hardware seems to have a race condition when changing modes. The + * current assumption is that the selection of the channel arrives + * earlier in the hardware than the mode bits when they are written at + * the same time. + * So set master mode for all channels as we do not support slave mode. + */ + ctrl |= SPI_IMX2_3_CTRL_MODE_MASK; /* set clock speed */ ctrl |= spi_imx2_3_clkdiv(spi_imx->spi_clk, config->speed_hz); -- cgit v1.2.2 From 1a5d81905aec1536783fb6ab875f31910f449941 Mon Sep 17 00:00:00 2001 From: Charulatha V Date: Wed, 2 Feb 2011 17:52:14 +0530 Subject: OMAP: devices: Modify McSPI device to adapt to hwmod framework Cleans up all base address definitions for omap_mcspi and adapts the device registration and driver to hwmod framework. Changes involves: 1) Removing all base address macro defines. 2) Using omap-device layer to register device and utilizing data from hwmod data file for base address, dma channel number, Irq_number, device attribute(number of chipselect). 3) Appending base address with pdata reg_offset for omap4 boards. For omap4 all regs used in driver deviate with reg_offset_macros defined with an value of 0x100. So pass this offset through pdata and append the same to base address retrieved from hwmod data file and we are not mapping *_HL_* regs which are not used in driver. Signed-off-by: Charulatha V Signed-off-by: Govindraj.R Acked-by: Grant Likely Reviewed-by: Partha Basak Reviewed-by: Kevin Hilman Signed-off-by: Tony Lindgren --- drivers/spi/omap2_mcspi.c | 112 ++++++++++++---------------------------------- 1 file changed, 29 insertions(+), 83 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index f076cc5c6fb0..f7851325c61e 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -3,7 +3,7 @@ * * Copyright (C) 2005, 2006 Nokia Corporation * Author: Samuel Ortiz and - * Juha Yrjölä + * Juha Yrj�l� * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1087,91 +1087,14 @@ static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi) return 0; } -static u8 __initdata spi1_rxdma_id [] = { - OMAP24XX_DMA_SPI1_RX0, - OMAP24XX_DMA_SPI1_RX1, - OMAP24XX_DMA_SPI1_RX2, - OMAP24XX_DMA_SPI1_RX3, -}; - -static u8 __initdata spi1_txdma_id [] = { - OMAP24XX_DMA_SPI1_TX0, - OMAP24XX_DMA_SPI1_TX1, - OMAP24XX_DMA_SPI1_TX2, - OMAP24XX_DMA_SPI1_TX3, -}; - -static u8 __initdata spi2_rxdma_id[] = { - OMAP24XX_DMA_SPI2_RX0, - OMAP24XX_DMA_SPI2_RX1, -}; - -static u8 __initdata spi2_txdma_id[] = { - OMAP24XX_DMA_SPI2_TX0, - OMAP24XX_DMA_SPI2_TX1, -}; - -#if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \ - || defined(CONFIG_ARCH_OMAP4) -static u8 __initdata spi3_rxdma_id[] = { - OMAP24XX_DMA_SPI3_RX0, - OMAP24XX_DMA_SPI3_RX1, -}; - -static u8 __initdata spi3_txdma_id[] = { - OMAP24XX_DMA_SPI3_TX0, - OMAP24XX_DMA_SPI3_TX1, -}; -#endif - -#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP4) -static u8 __initdata spi4_rxdma_id[] = { - OMAP34XX_DMA_SPI4_RX0, -}; - -static u8 __initdata spi4_txdma_id[] = { - OMAP34XX_DMA_SPI4_TX0, -}; -#endif static int __init omap2_mcspi_probe(struct platform_device *pdev) { struct spi_master *master; + struct omap2_mcspi_platform_config *pdata = pdev->dev.platform_data; struct omap2_mcspi *mcspi; struct resource *r; int status = 0, i; - const u8 *rxdma_id, *txdma_id; - unsigned num_chipselect; - - switch (pdev->id) { - case 1: - rxdma_id = spi1_rxdma_id; - txdma_id = spi1_txdma_id; - num_chipselect = 4; - break; - case 2: - rxdma_id = spi2_rxdma_id; - txdma_id = spi2_txdma_id; - num_chipselect = 2; - break; -#if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \ - || defined(CONFIG_ARCH_OMAP4) - case 3: - rxdma_id = spi3_rxdma_id; - txdma_id = spi3_txdma_id; - num_chipselect = 2; - break; -#endif -#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP4) - case 4: - rxdma_id = spi4_rxdma_id; - txdma_id = spi4_txdma_id; - num_chipselect = 1; - break; -#endif - default: - return -EINVAL; - } master = spi_alloc_master(&pdev->dev, sizeof *mcspi); if (master == NULL) { @@ -1188,7 +1111,7 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) master->setup = omap2_mcspi_setup; master->transfer = omap2_mcspi_transfer; master->cleanup = omap2_mcspi_cleanup; - master->num_chipselect = num_chipselect; + master->num_chipselect = pdata->num_cs; dev_set_drvdata(&pdev->dev, master); @@ -1206,6 +1129,8 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) goto err1; } + r->start += pdata->regs_offset; + r->end += pdata->regs_offset; mcspi->phys = r->start; mcspi->base = ioremap(r->start, r->end - r->start + 1); if (!mcspi->base) { @@ -1240,11 +1165,32 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) if (mcspi->dma_channels == NULL) goto err3; - for (i = 0; i < num_chipselect; i++) { + for (i = 0; i < master->num_chipselect; i++) { + char dma_ch_name[14]; + struct resource *dma_res; + + sprintf(dma_ch_name, "rx%d", i); + dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, + dma_ch_name); + if (!dma_res) { + dev_dbg(&pdev->dev, "cannot get DMA RX channel\n"); + status = -ENODEV; + break; + } + mcspi->dma_channels[i].dma_rx_channel = -1; - mcspi->dma_channels[i].dma_rx_sync_dev = rxdma_id[i]; + mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start; + sprintf(dma_ch_name, "tx%d", i); + dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, + dma_ch_name); + if (!dma_res) { + dev_dbg(&pdev->dev, "cannot get DMA TX channel\n"); + status = -ENODEV; + break; + } + mcspi->dma_channels[i].dma_tx_channel = -1; - mcspi->dma_channels[i].dma_tx_sync_dev = txdma_id[i]; + mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start; } if (omap2_mcspi_reset(mcspi) < 0) -- cgit v1.2.2 From 1f1a4384b6500273020e48130f73601e4ba7d84b Mon Sep 17 00:00:00 2001 From: "Govindraj.R" Date: Wed, 2 Feb 2011 17:52:15 +0530 Subject: OMAP: runtime: McSPI driver runtime conversion McSPI runtime conversion. Changes involves: 1) remove clock framework apis to use runtime framework apis. 2) context restore from runtime resume which is a callback for get_sync. 3) Remove SYSCONFIG(sysc) register handling (a) Remove context save and restore of sysc reg and remove soft reset done from sysc reg as this will be done with hwmod framework. (b) Also cleanup sysc reg bit macros. 4) Rename the omap2_mcspi_reset function to omap2_mcspi_master_setup function as with hwmod changes soft reset will be done in hwmod framework itself and use the return value from clock enable function to return for failure scenarios. Signed-off-by: Charulatha V Signed-off-by: Govindraj.R Acked-by: Grant Likely Reviewed-by: Partha Basak Reviewed-by: Kevin Hilman Signed-off-by: Tony Lindgren --- drivers/spi/omap2_mcspi.c | 116 +++++++++++++++++----------------------------- 1 file changed, 42 insertions(+), 74 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index f7851325c61e..36501adc125d 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -46,7 +47,6 @@ #define OMAP2_MCSPI_MAX_CTRL 4 #define OMAP2_MCSPI_REVISION 0x00 -#define OMAP2_MCSPI_SYSCONFIG 0x10 #define OMAP2_MCSPI_SYSSTATUS 0x14 #define OMAP2_MCSPI_IRQSTATUS 0x18 #define OMAP2_MCSPI_IRQENABLE 0x1c @@ -63,13 +63,6 @@ /* per-register bitmasks: */ -#define OMAP2_MCSPI_SYSCONFIG_SMARTIDLE BIT(4) -#define OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP BIT(2) -#define OMAP2_MCSPI_SYSCONFIG_AUTOIDLE BIT(0) -#define OMAP2_MCSPI_SYSCONFIG_SOFTRESET BIT(1) - -#define OMAP2_MCSPI_SYSSTATUS_RESETDONE BIT(0) - #define OMAP2_MCSPI_MODULCTRL_SINGLE BIT(0) #define OMAP2_MCSPI_MODULCTRL_MS BIT(2) #define OMAP2_MCSPI_MODULCTRL_STEST BIT(3) @@ -122,13 +115,12 @@ struct omap2_mcspi { spinlock_t lock; struct list_head msg_queue; struct spi_master *master; - struct clk *ick; - struct clk *fck; /* Virtual base address of the controller */ void __iomem *base; unsigned long phys; /* SPI1 has 4 channels, while SPI2 has 2 */ struct omap2_mcspi_dma *dma_channels; + struct device *dev; }; struct omap2_mcspi_cs { @@ -144,7 +136,6 @@ struct omap2_mcspi_cs { * corresponding registers are modified. */ struct omap2_mcspi_regs { - u32 sysconfig; u32 modulctrl; u32 wakeupenable; struct list_head cs; @@ -268,9 +259,6 @@ static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_MODULCTRL, omap2_mcspi_ctx[spi_cntrl->bus_num - 1].modulctrl); - mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_SYSCONFIG, - omap2_mcspi_ctx[spi_cntrl->bus_num - 1].sysconfig); - mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_WAKEUPENABLE, omap2_mcspi_ctx[spi_cntrl->bus_num - 1].wakeupenable); @@ -280,20 +268,12 @@ static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) } static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi) { - clk_disable(mcspi->ick); - clk_disable(mcspi->fck); + pm_runtime_put_sync(mcspi->dev); } static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi) { - if (clk_enable(mcspi->ick)) - return -ENODEV; - if (clk_enable(mcspi->fck)) - return -ENODEV; - - omap2_mcspi_restore_ctx(mcspi); - - return 0; + return pm_runtime_get_sync(mcspi->dev); } static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) @@ -819,8 +799,9 @@ static int omap2_mcspi_setup(struct spi_device *spi) return ret; } - if (omap2_mcspi_enable_clocks(mcspi)) - return -ENODEV; + ret = omap2_mcspi_enable_clocks(mcspi); + if (ret < 0) + return ret; ret = omap2_mcspi_setup_transfer(spi, NULL); omap2_mcspi_disable_clocks(mcspi); @@ -863,10 +844,11 @@ static void omap2_mcspi_work(struct work_struct *work) struct omap2_mcspi *mcspi; mcspi = container_of(work, struct omap2_mcspi, work); - spin_lock_irq(&mcspi->lock); - if (omap2_mcspi_enable_clocks(mcspi)) - goto out; + if (omap2_mcspi_enable_clocks(mcspi) < 0) + return; + + spin_lock_irq(&mcspi->lock); /* We only enable one channel at a time -- the one whose message is * at the head of the queue -- although this controller would gladly @@ -979,10 +961,9 @@ static void omap2_mcspi_work(struct work_struct *work) spin_lock_irq(&mcspi->lock); } - omap2_mcspi_disable_clocks(mcspi); - -out: spin_unlock_irq(&mcspi->lock); + + omap2_mcspi_disable_clocks(mcspi); } static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) @@ -1058,25 +1039,15 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) return 0; } -static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi) +static int __init omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) { struct spi_master *master = mcspi->master; u32 tmp; + int ret = 0; - if (omap2_mcspi_enable_clocks(mcspi)) - return -1; - - mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, - OMAP2_MCSPI_SYSCONFIG_SOFTRESET); - do { - tmp = mcspi_read_reg(master, OMAP2_MCSPI_SYSSTATUS); - } while (!(tmp & OMAP2_MCSPI_SYSSTATUS_RESETDONE)); - - tmp = OMAP2_MCSPI_SYSCONFIG_AUTOIDLE | - OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP | - OMAP2_MCSPI_SYSCONFIG_SMARTIDLE; - mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, tmp); - omap2_mcspi_ctx[master->bus_num - 1].sysconfig = tmp; + ret = omap2_mcspi_enable_clocks(mcspi); + if (ret < 0) + return ret; tmp = OMAP2_MCSPI_WAKEUPENABLE_WKEN; mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, tmp); @@ -1087,6 +1058,18 @@ static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi) return 0; } +static int omap_mcspi_runtime_resume(struct device *dev) +{ + struct omap2_mcspi *mcspi; + struct spi_master *master; + + master = dev_get_drvdata(dev); + mcspi = spi_master_get_devdata(master); + omap2_mcspi_restore_ctx(mcspi); + + return 0; +} + static int __init omap2_mcspi_probe(struct platform_device *pdev) { @@ -1136,34 +1119,22 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) if (!mcspi->base) { dev_dbg(&pdev->dev, "can't ioremap MCSPI\n"); status = -ENOMEM; - goto err1aa; + goto err2; } + mcspi->dev = &pdev->dev; INIT_WORK(&mcspi->work, omap2_mcspi_work); spin_lock_init(&mcspi->lock); INIT_LIST_HEAD(&mcspi->msg_queue); INIT_LIST_HEAD(&omap2_mcspi_ctx[master->bus_num - 1].cs); - mcspi->ick = clk_get(&pdev->dev, "ick"); - if (IS_ERR(mcspi->ick)) { - dev_dbg(&pdev->dev, "can't get mcspi_ick\n"); - status = PTR_ERR(mcspi->ick); - goto err1a; - } - mcspi->fck = clk_get(&pdev->dev, "fck"); - if (IS_ERR(mcspi->fck)) { - dev_dbg(&pdev->dev, "can't get mcspi_fck\n"); - status = PTR_ERR(mcspi->fck); - goto err2; - } - mcspi->dma_channels = kcalloc(master->num_chipselect, sizeof(struct omap2_mcspi_dma), GFP_KERNEL); if (mcspi->dma_channels == NULL) - goto err3; + goto err2; for (i = 0; i < master->num_chipselect; i++) { char dma_ch_name[14]; @@ -1193,8 +1164,10 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start; } - if (omap2_mcspi_reset(mcspi) < 0) - goto err4; + pm_runtime_enable(&pdev->dev); + + if (status || omap2_mcspi_master_setup(mcspi) < 0) + goto err3; status = spi_register_master(master); if (status < 0) @@ -1203,17 +1176,13 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) return status; err4: - kfree(mcspi->dma_channels); + spi_master_put(master); err3: - clk_put(mcspi->fck); + kfree(mcspi->dma_channels); err2: - clk_put(mcspi->ick); -err1a: - iounmap(mcspi->base); -err1aa: release_mem_region(r->start, (r->end - r->start) + 1); + iounmap(mcspi->base); err1: - spi_master_put(master); return status; } @@ -1229,9 +1198,7 @@ static int __exit omap2_mcspi_remove(struct platform_device *pdev) mcspi = spi_master_get_devdata(master); dma_channels = mcspi->dma_channels; - clk_put(mcspi->fck); - clk_put(mcspi->ick); - + omap2_mcspi_disable_clocks(mcspi); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(r->start, (r->end - r->start) + 1); @@ -1282,6 +1249,7 @@ static int omap2_mcspi_resume(struct device *dev) static const struct dev_pm_ops omap2_mcspi_pm_ops = { .resume = omap2_mcspi_resume, + .runtime_resume = omap_mcspi_runtime_resume, }; static struct platform_driver omap2_mcspi_driver = { -- cgit v1.2.2 From ce792580ea2ce6f7259b45124e9ccc4574c31606 Mon Sep 17 00:00:00 2001 From: Thomas Chou Date: Mon, 14 Feb 2011 10:20:39 +0800 Subject: spi: add OpenCores tiny SPI driver This patch adds support of OpenCores tiny SPI driver. http://opencores.org/project,tiny_spi Signed-off-by: Thomas Chou Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi_oc_tiny.c | 425 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 433 insertions(+) create mode 100644 drivers/spi/spi_oc_tiny.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bb233a9cbad2..8faa57a58dd1 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -231,6 +231,13 @@ config SPI_FSL_ESPI From MPC8536, 85xx platform uses the controller, and all P10xx, P20xx, P30xx,P40xx, P50xx uses this controller. +config SPI_OC_TINY + tristate "OpenCores tiny SPI" + depends on GENERIC_GPIO + select SPI_BITBANG + help + This is the driver for OpenCores tiny SPI master controller. + config SPI_OMAP_UWIRE tristate "OMAP1 MicroWire" depends on ARCH_OMAP1 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 86d1b5f9bbd9..c7e4c23c6f36 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_PXA2XX_PCI) += pxa2xx_spi_pci.o +obj-$(CONFIG_SPI_OC_TINY) += spi_oc_tiny.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o diff --git a/drivers/spi/spi_oc_tiny.c b/drivers/spi/spi_oc_tiny.c new file mode 100644 index 000000000000..f1bde66cea19 --- /dev/null +++ b/drivers/spi/spi_oc_tiny.c @@ -0,0 +1,425 @@ +/* + * OpenCores tiny SPI master driver + * + * http://opencores.org/project,tiny_spi + * + * Copyright (C) 2011 Thomas Chou + * + * Based on spi_s3c24xx.c, which is: + * Copyright (c) 2006 Ben Dooks + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "spi_oc_tiny" + +#define TINY_SPI_RXDATA 0 +#define TINY_SPI_TXDATA 4 +#define TINY_SPI_STATUS 8 +#define TINY_SPI_CONTROL 12 +#define TINY_SPI_BAUD 16 + +#define TINY_SPI_STATUS_TXE 0x1 +#define TINY_SPI_STATUS_TXR 0x2 + +struct tiny_spi { + /* bitbang has to be first */ + struct spi_bitbang bitbang; + struct completion done; + + void __iomem *base; + int irq; + unsigned int freq; + unsigned int baudwidth; + unsigned int baud; + unsigned int speed_hz; + unsigned int mode; + unsigned int len; + unsigned int txc, rxc; + const u8 *txp; + u8 *rxp; + unsigned int gpio_cs_count; + int *gpio_cs; +}; + +static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev) +{ + return spi_master_get_devdata(sdev->master); +} + +static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz) +{ + struct tiny_spi *hw = tiny_spi_to_hw(spi); + + return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1; +} + +static void tiny_spi_chipselect(struct spi_device *spi, int is_active) +{ + struct tiny_spi *hw = tiny_spi_to_hw(spi); + + if (hw->gpio_cs_count) { + gpio_set_value(hw->gpio_cs[spi->chip_select], + (spi->mode & SPI_CS_HIGH) ? is_active : !is_active); + } +} + +static int tiny_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct tiny_spi *hw = tiny_spi_to_hw(spi); + unsigned int baud = hw->baud; + + if (t) { + if (t->speed_hz && t->speed_hz != hw->speed_hz) + baud = tiny_spi_baud(spi, t->speed_hz); + } + writel(baud, hw->base + TINY_SPI_BAUD); + writel(hw->mode, hw->base + TINY_SPI_CONTROL); + return 0; +} + +static int tiny_spi_setup(struct spi_device *spi) +{ + struct tiny_spi *hw = tiny_spi_to_hw(spi); + + if (spi->max_speed_hz != hw->speed_hz) { + hw->speed_hz = spi->max_speed_hz; + hw->baud = tiny_spi_baud(spi, hw->speed_hz); + } + hw->mode = spi->mode & (SPI_CPOL | SPI_CPHA); + return 0; +} + +static inline void tiny_spi_wait_txr(struct tiny_spi *hw) +{ + while (!(readb(hw->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXR)) + cpu_relax(); +} + +static inline void tiny_spi_wait_txe(struct tiny_spi *hw) +{ + while (!(readb(hw->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXE)) + cpu_relax(); +} + +static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) +{ + struct tiny_spi *hw = tiny_spi_to_hw(spi); + const u8 *txp = t->tx_buf; + u8 *rxp = t->rx_buf; + unsigned int i; + + if (hw->irq >= 0) { + /* use intrrupt driven data transfer */ + hw->len = t->len; + hw->txp = t->tx_buf; + hw->rxp = t->rx_buf; + hw->txc = 0; + hw->rxc = 0; + + /* send the first byte */ + if (t->len > 1) { + writeb(hw->txp ? *hw->txp++ : 0, + hw->base + TINY_SPI_TXDATA); + hw->txc++; + writeb(hw->txp ? *hw->txp++ : 0, + hw->base + TINY_SPI_TXDATA); + hw->txc++; + writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS); + } else { + writeb(hw->txp ? *hw->txp++ : 0, + hw->base + TINY_SPI_TXDATA); + hw->txc++; + writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS); + } + + wait_for_completion(&hw->done); + } else if (txp && rxp) { + /* we need to tighten the transfer loop */ + writeb(*txp++, hw->base + TINY_SPI_TXDATA); + if (t->len > 1) { + writeb(*txp++, hw->base + TINY_SPI_TXDATA); + for (i = 2; i < t->len; i++) { + u8 rx, tx = *txp++; + tiny_spi_wait_txr(hw); + rx = readb(hw->base + TINY_SPI_TXDATA); + writeb(tx, hw->base + TINY_SPI_TXDATA); + *rxp++ = rx; + } + tiny_spi_wait_txr(hw); + *rxp++ = readb(hw->base + TINY_SPI_TXDATA); + } + tiny_spi_wait_txe(hw); + *rxp++ = readb(hw->base + TINY_SPI_RXDATA); + } else if (rxp) { + writeb(0, hw->base + TINY_SPI_TXDATA); + if (t->len > 1) { + writeb(0, + hw->base + TINY_SPI_TXDATA); + for (i = 2; i < t->len; i++) { + u8 rx; + tiny_spi_wait_txr(hw); + rx = readb(hw->base + TINY_SPI_TXDATA); + writeb(0, hw->base + TINY_SPI_TXDATA); + *rxp++ = rx; + } + tiny_spi_wait_txr(hw); + *rxp++ = readb(hw->base + TINY_SPI_TXDATA); + } + tiny_spi_wait_txe(hw); + *rxp++ = readb(hw->base + TINY_SPI_RXDATA); + } else if (txp) { + writeb(*txp++, hw->base + TINY_SPI_TXDATA); + if (t->len > 1) { + writeb(*txp++, hw->base + TINY_SPI_TXDATA); + for (i = 2; i < t->len; i++) { + u8 tx = *txp++; + tiny_spi_wait_txr(hw); + writeb(tx, hw->base + TINY_SPI_TXDATA); + } + } + tiny_spi_wait_txe(hw); + } else { + writeb(0, hw->base + TINY_SPI_TXDATA); + if (t->len > 1) { + writeb(0, hw->base + TINY_SPI_TXDATA); + for (i = 2; i < t->len; i++) { + tiny_spi_wait_txr(hw); + writeb(0, hw->base + TINY_SPI_TXDATA); + } + } + tiny_spi_wait_txe(hw); + } + return t->len; +} + +static irqreturn_t tiny_spi_irq(int irq, void *dev) +{ + struct tiny_spi *hw = dev; + + writeb(0, hw->base + TINY_SPI_STATUS); + if (hw->rxc + 1 == hw->len) { + if (hw->rxp) + *hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA); + hw->rxc++; + complete(&hw->done); + } else { + if (hw->rxp) + *hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA); + hw->rxc++; + if (hw->txc < hw->len) { + writeb(hw->txp ? *hw->txp++ : 0, + hw->base + TINY_SPI_TXDATA); + hw->txc++; + writeb(TINY_SPI_STATUS_TXR, + hw->base + TINY_SPI_STATUS); + } else { + writeb(TINY_SPI_STATUS_TXE, + hw->base + TINY_SPI_STATUS); + } + } + return IRQ_HANDLED; +} + +#ifdef CONFIG_OF +#include + +static int __devinit tiny_spi_of_probe(struct platform_device *pdev) +{ + struct tiny_spi *hw = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; + unsigned int i; + const __be32 *val; + int len; + + if (!np) + return 0; + hw->gpio_cs_count = of_gpio_count(np); + if (hw->gpio_cs_count) { + hw->gpio_cs = devm_kzalloc(&pdev->dev, + hw->gpio_cs_count * sizeof(unsigned int), + GFP_KERNEL); + if (!hw->gpio_cs) + return -ENOMEM; + } + for (i = 0; i < hw->gpio_cs_count; i++) { + hw->gpio_cs[i] = of_get_gpio_flags(np, i, NULL); + if (hw->gpio_cs[i] < 0) + return -ENODEV; + } + hw->bitbang.master->dev.of_node = pdev->dev.of_node; + val = of_get_property(pdev->dev.of_node, + "clock-frequency", &len); + if (val && len >= sizeof(__be32)) + hw->freq = be32_to_cpup(val); + val = of_get_property(pdev->dev.of_node, "baud-width", &len); + if (val && len >= sizeof(__be32)) + hw->baudwidth = be32_to_cpup(val); + return 0; +} +#else /* !CONFIG_OF */ +static int __devinit tiny_spi_of_probe(struct platform_device *pdev) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int __devinit tiny_spi_probe(struct platform_device *pdev) +{ + struct tiny_spi_platform_data *platp = pdev->dev.platform_data; + struct tiny_spi *hw; + struct spi_master *master; + struct resource *res; + unsigned int i; + int err = -ENODEV; + + master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi)); + if (!master) + return err; + + /* setup the master state. */ + master->bus_num = pdev->id; + master->num_chipselect = 255; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->setup = tiny_spi_setup; + + hw = spi_master_get_devdata(master); + platform_set_drvdata(pdev, hw); + + /* setup the state for the bitbang driver */ + hw->bitbang.master = spi_master_get(master); + if (!hw->bitbang.master) + return err; + hw->bitbang.setup_transfer = tiny_spi_setup_transfer; + hw->bitbang.chipselect = tiny_spi_chipselect; + hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs; + + /* find and map our resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + goto exit_busy; + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) + goto exit_busy; + hw->base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!hw->base) + goto exit_busy; + /* irq is optional */ + hw->irq = platform_get_irq(pdev, 0); + if (hw->irq >= 0) { + init_completion(&hw->done); + err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0, + pdev->name, hw); + if (err) + goto exit; + } + /* find platform data */ + if (platp) { + hw->gpio_cs_count = platp->gpio_cs_count; + hw->gpio_cs = platp->gpio_cs; + if (platp->gpio_cs_count && !platp->gpio_cs) + goto exit_busy; + hw->freq = platp->freq; + hw->baudwidth = platp->baudwidth; + } else { + err = tiny_spi_of_probe(pdev); + if (err) + goto exit; + } + for (i = 0; i < hw->gpio_cs_count; i++) { + err = gpio_request(hw->gpio_cs[i], dev_name(&pdev->dev)); + if (err) + goto exit_gpio; + gpio_direction_output(hw->gpio_cs[i], 1); + } + hw->bitbang.master->num_chipselect = max(1U, hw->gpio_cs_count); + + /* register our spi controller */ + err = spi_bitbang_start(&hw->bitbang); + if (err) + goto exit; + dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); + + return 0; + +exit_gpio: + while (i-- > 0) + gpio_free(hw->gpio_cs[i]); +exit_busy: + err = -EBUSY; +exit: + platform_set_drvdata(pdev, NULL); + spi_master_put(master); + return err; +} + +static int __devexit tiny_spi_remove(struct platform_device *pdev) +{ + struct tiny_spi *hw = platform_get_drvdata(pdev); + struct spi_master *master = hw->bitbang.master; + unsigned int i; + + spi_bitbang_stop(&hw->bitbang); + for (i = 0; i < hw->gpio_cs_count; i++) + gpio_free(hw->gpio_cs[i]); + platform_set_drvdata(pdev, NULL); + spi_master_put(master); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id tiny_spi_match[] = { + { .compatible = "opencores,tiny-spi-rtlsvn2", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tiny_spi_match); +#else /* CONFIG_OF */ +#define tiny_spi_match NULL +#endif /* CONFIG_OF */ + +static struct platform_driver tiny_spi_driver = { + .probe = tiny_spi_probe, + .remove = __devexit_p(tiny_spi_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = NULL, + .of_match_table = tiny_spi_match, + }, +}; + +static int __init tiny_spi_init(void) +{ + return platform_driver_register(&tiny_spi_driver); +} +module_init(tiny_spi_init); + +static void __exit tiny_spi_exit(void) +{ + platform_driver_unregister(&tiny_spi_driver); +} +module_exit(tiny_spi_exit); + +MODULE_DESCRIPTION("OpenCores tiny SPI driver"); +MODULE_AUTHOR("Thomas Chou "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.2.2 From 0b782531c038d4a4bded3fc1069c961b1f14f0de Mon Sep 17 00:00:00 2001 From: Thomas Chou Date: Mon, 14 Feb 2011 10:10:43 +0800 Subject: spi: New driver for Altera SPI This patch adds a new SPI driver to support the Altera SOPC Builder SPI component. It uses the bitbanging library. Signed-off-by: Thomas Chou Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi_altera.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+) create mode 100644 drivers/spi/spi_altera.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8faa57a58dd1..e85b248800c7 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -53,6 +53,12 @@ if SPI_MASTER comment "SPI Master Controller Drivers" +config SPI_ALTERA + tristate "Altera SPI Controller" + select SPI_BITBANG + help + This is the driver for the Altera SPI Controller. + config SPI_ATH79 tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver" depends on ATH79 && GENERIC_GPIO diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c7e4c23c6f36..e111a0f322a9 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -9,6 +9,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG obj-$(CONFIG_SPI_MASTER) += spi.o # SPI master controller drivers (bus) +obj-$(CONFIG_SPI_ALTERA) += spi_altera.o obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o obj-$(CONFIG_SPI_ATH79) += ath79_spi.o obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o diff --git a/drivers/spi/spi_altera.c b/drivers/spi/spi_altera.c new file mode 100644 index 000000000000..4813a63ce6fb --- /dev/null +++ b/drivers/spi/spi_altera.c @@ -0,0 +1,339 @@ +/* + * Altera SPI driver + * + * Copyright (C) 2008 Thomas Chou + * + * Based on spi_s3c24xx.c, which is: + * Copyright (c) 2006 Ben Dooks + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "spi_altera" + +#define ALTERA_SPI_RXDATA 0 +#define ALTERA_SPI_TXDATA 4 +#define ALTERA_SPI_STATUS 8 +#define ALTERA_SPI_CONTROL 12 +#define ALTERA_SPI_SLAVE_SEL 20 + +#define ALTERA_SPI_STATUS_ROE_MSK 0x8 +#define ALTERA_SPI_STATUS_TOE_MSK 0x10 +#define ALTERA_SPI_STATUS_TMT_MSK 0x20 +#define ALTERA_SPI_STATUS_TRDY_MSK 0x40 +#define ALTERA_SPI_STATUS_RRDY_MSK 0x80 +#define ALTERA_SPI_STATUS_E_MSK 0x100 + +#define ALTERA_SPI_CONTROL_IROE_MSK 0x8 +#define ALTERA_SPI_CONTROL_ITOE_MSK 0x10 +#define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40 +#define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80 +#define ALTERA_SPI_CONTROL_IE_MSK 0x100 +#define ALTERA_SPI_CONTROL_SSO_MSK 0x400 + +struct altera_spi { + /* bitbang has to be first */ + struct spi_bitbang bitbang; + struct completion done; + + void __iomem *base; + int irq; + int len; + int count; + int bytes_per_word; + unsigned long imr; + + /* data buffers */ + const unsigned char *tx; + unsigned char *rx; +}; + +static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev) +{ + return spi_master_get_devdata(sdev->master); +} + +static void altera_spi_chipsel(struct spi_device *spi, int value) +{ + struct altera_spi *hw = altera_spi_to_hw(spi); + + if (spi->mode & SPI_CS_HIGH) { + switch (value) { + case BITBANG_CS_INACTIVE: + writel(1 << spi->chip_select, + hw->base + ALTERA_SPI_SLAVE_SEL); + hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; + writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); + break; + + case BITBANG_CS_ACTIVE: + hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; + writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); + writel(0, hw->base + ALTERA_SPI_SLAVE_SEL); + break; + } + } else { + switch (value) { + case BITBANG_CS_INACTIVE: + hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; + writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); + break; + + case BITBANG_CS_ACTIVE: + writel(1 << spi->chip_select, + hw->base + ALTERA_SPI_SLAVE_SEL); + hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; + writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); + break; + } + } +} + +static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) +{ + return 0; +} + +static int altera_spi_setup(struct spi_device *spi) +{ + return 0; +} + +static inline unsigned int hw_txbyte(struct altera_spi *hw, int count) +{ + if (hw->tx) { + switch (hw->bytes_per_word) { + case 1: + return hw->tx[count]; + case 2: + return (hw->tx[count * 2] + | (hw->tx[count * 2 + 1] << 8)); + } + } + return 0; +} + +static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t) +{ + struct altera_spi *hw = altera_spi_to_hw(spi); + + hw->tx = t->tx_buf; + hw->rx = t->rx_buf; + hw->count = 0; + hw->bytes_per_word = (t->bits_per_word ? : spi->bits_per_word) / 8; + hw->len = t->len / hw->bytes_per_word; + + if (hw->irq >= 0) { + /* enable receive interrupt */ + hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK; + writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); + + /* send the first byte */ + writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA); + + wait_for_completion(&hw->done); + /* disable receive interrupt */ + hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; + writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); + } else { + /* send the first byte */ + writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA); + + while (1) { + unsigned int rxd; + + while (!(readl(hw->base + ALTERA_SPI_STATUS) & + ALTERA_SPI_STATUS_RRDY_MSK)) + cpu_relax(); + + rxd = readl(hw->base + ALTERA_SPI_RXDATA); + if (hw->rx) { + switch (hw->bytes_per_word) { + case 1: + hw->rx[hw->count] = rxd; + break; + case 2: + hw->rx[hw->count * 2] = rxd; + hw->rx[hw->count * 2 + 1] = rxd >> 8; + break; + } + } + + hw->count++; + + if (hw->count < hw->len) + writel(hw_txbyte(hw, hw->count), + hw->base + ALTERA_SPI_TXDATA); + else + break; + } + + } + + return hw->count * hw->bytes_per_word; +} + +static irqreturn_t altera_spi_irq(int irq, void *dev) +{ + struct altera_spi *hw = dev; + unsigned int rxd; + + rxd = readl(hw->base + ALTERA_SPI_RXDATA); + if (hw->rx) { + switch (hw->bytes_per_word) { + case 1: + hw->rx[hw->count] = rxd; + break; + case 2: + hw->rx[hw->count * 2] = rxd; + hw->rx[hw->count * 2 + 1] = rxd >> 8; + break; + } + } + + hw->count++; + + if (hw->count < hw->len) + writel(hw_txbyte(hw, hw->count), hw->base + ALTERA_SPI_TXDATA); + else + complete(&hw->done); + + return IRQ_HANDLED; +} + +static int __devinit altera_spi_probe(struct platform_device *pdev) +{ + struct altera_spi_platform_data *platp = pdev->dev.platform_data; + struct altera_spi *hw; + struct spi_master *master; + struct resource *res; + int err = -ENODEV; + + master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi)); + if (!master) + return err; + + /* setup the master state. */ + master->bus_num = pdev->id; + master->num_chipselect = 16; + master->mode_bits = SPI_CS_HIGH; + master->setup = altera_spi_setup; + + hw = spi_master_get_devdata(master); + platform_set_drvdata(pdev, hw); + + /* setup the state for the bitbang driver */ + hw->bitbang.master = spi_master_get(master); + if (!hw->bitbang.master) + return err; + hw->bitbang.setup_transfer = altera_spi_setupxfer; + hw->bitbang.chipselect = altera_spi_chipsel; + hw->bitbang.txrx_bufs = altera_spi_txrx; + + /* find and map our resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + goto exit_busy; + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) + goto exit_busy; + hw->base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!hw->base) + goto exit_busy; + /* program defaults into the registers */ + hw->imr = 0; /* disable spi interrupts */ + writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); + writel(0, hw->base + ALTERA_SPI_STATUS); /* clear status reg */ + if (readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK) + readl(hw->base + ALTERA_SPI_RXDATA); /* flush rxdata */ + /* irq is optional */ + hw->irq = platform_get_irq(pdev, 0); + if (hw->irq >= 0) { + init_completion(&hw->done); + err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0, + pdev->name, hw); + if (err) + goto exit; + } + /* find platform data */ + if (!platp) + hw->bitbang.master->dev.of_node = pdev->dev.of_node; + + /* register our spi controller */ + err = spi_bitbang_start(&hw->bitbang); + if (err) + goto exit; + dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); + + return 0; + +exit_busy: + err = -EBUSY; +exit: + platform_set_drvdata(pdev, NULL); + spi_master_put(master); + return err; +} + +static int __devexit altera_spi_remove(struct platform_device *dev) +{ + struct altera_spi *hw = platform_get_drvdata(dev); + struct spi_master *master = hw->bitbang.master; + + spi_bitbang_stop(&hw->bitbang); + platform_set_drvdata(dev, NULL); + spi_master_put(master); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id altera_spi_match[] = { + { .compatible = "ALTR,spi-1.0", }, + {}, +}; +MODULE_DEVICE_TABLE(of, altera_spi_match); +#else /* CONFIG_OF */ +#define altera_spi_match NULL +#endif /* CONFIG_OF */ + +static struct platform_driver altera_spi_driver = { + .probe = altera_spi_probe, + .remove = __devexit_p(altera_spi_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = NULL, + .of_match_table = altera_spi_match, + }, +}; + +static int __init altera_spi_init(void) +{ + return platform_driver_register(&altera_spi_driver); +} +module_init(altera_spi_init); + +static void __exit altera_spi_exit(void) +{ + platform_driver_unregister(&altera_spi_driver); +} +module_exit(altera_spi_exit); + +MODULE_DESCRIPTION("Altera SPI driver"); +MODULE_AUTHOR("Thomas Chou "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.2.2 From 5c05dd0750402f174302cf2b8cdf4111be080dcb Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 15 Feb 2011 10:30:32 +0900 Subject: spi: add support for SuperH SPI The SH7757 has SPI0 module. This patch supports it. Signed-off-by: Yoshihiro Shimoda [grant.likely@secretlab.ca: fixed Makefile ordering, added __dev{init,exit} annotations, removed DRIVER_VERSION (this is mainline, the version == the kernel version) and tidied some indentation & style stuff] Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi_sh.c | 543 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 550 insertions(+) create mode 100644 drivers/spi/spi_sh.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index e85b248800c7..cdeb01f45bc2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -343,6 +343,12 @@ config SPI_SH_MSIOF help SPI driver for SuperH MSIOF blocks. +config SPI_SH + tristate "SuperH SPI controller" + depends on SUPERH + help + SPI driver for SuperH SPI blocks. + config SPI_SH_SCI tristate "SuperH SCI SPI controller" depends on SUPERH diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e111a0f322a9..8b5a315045a6 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_SPI_TEGRA) += spi_tegra.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o +obj-$(CONFIG_SPI_SH) += spi_sh.o obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o diff --git a/drivers/spi/spi_sh.c b/drivers/spi/spi_sh.c new file mode 100644 index 000000000000..869a07d375d6 --- /dev/null +++ b/drivers/spi/spi_sh.c @@ -0,0 +1,543 @@ +/* + * SH SPI bus driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * + * Based on pxa2xx_spi.c: + * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPI_SH_TBR 0x00 +#define SPI_SH_RBR 0x00 +#define SPI_SH_CR1 0x08 +#define SPI_SH_CR2 0x10 +#define SPI_SH_CR3 0x18 +#define SPI_SH_CR4 0x20 +#define SPI_SH_CR5 0x28 + +/* CR1 */ +#define SPI_SH_TBE 0x80 +#define SPI_SH_TBF 0x40 +#define SPI_SH_RBE 0x20 +#define SPI_SH_RBF 0x10 +#define SPI_SH_PFONRD 0x08 +#define SPI_SH_SSDB 0x04 +#define SPI_SH_SSD 0x02 +#define SPI_SH_SSA 0x01 + +/* CR2 */ +#define SPI_SH_RSTF 0x80 +#define SPI_SH_LOOPBK 0x40 +#define SPI_SH_CPOL 0x20 +#define SPI_SH_CPHA 0x10 +#define SPI_SH_L1M0 0x08 + +/* CR3 */ +#define SPI_SH_MAX_BYTE 0xFF + +/* CR4 */ +#define SPI_SH_TBEI 0x80 +#define SPI_SH_TBFI 0x40 +#define SPI_SH_RBEI 0x20 +#define SPI_SH_RBFI 0x10 +#define SPI_SH_WPABRT 0x04 +#define SPI_SH_SSS 0x01 + +/* CR8 */ +#define SPI_SH_P1L0 0x80 +#define SPI_SH_PP1L0 0x40 +#define SPI_SH_MUXI 0x20 +#define SPI_SH_MUXIRQ 0x10 + +#define SPI_SH_FIFO_SIZE 32 +#define SPI_SH_SEND_TIMEOUT (3 * HZ) +#define SPI_SH_RECEIVE_TIMEOUT (HZ >> 3) + +#undef DEBUG + +struct spi_sh_data { + void __iomem *addr; + int irq; + struct spi_master *master; + struct list_head queue; + struct workqueue_struct *workqueue; + struct work_struct ws; + unsigned long cr1; + wait_queue_head_t wait; + spinlock_t lock; +}; + +static void spi_sh_write(struct spi_sh_data *ss, unsigned long data, + unsigned long offset) +{ + writel(data, ss->addr + offset); +} + +static unsigned long spi_sh_read(struct spi_sh_data *ss, unsigned long offset) +{ + return readl(ss->addr + offset); +} + +static void spi_sh_set_bit(struct spi_sh_data *ss, unsigned long val, + unsigned long offset) +{ + unsigned long tmp; + + tmp = spi_sh_read(ss, offset); + tmp |= val; + spi_sh_write(ss, tmp, offset); +} + +static void spi_sh_clear_bit(struct spi_sh_data *ss, unsigned long val, + unsigned long offset) +{ + unsigned long tmp; + + tmp = spi_sh_read(ss, offset); + tmp &= ~val; + spi_sh_write(ss, tmp, offset); +} + +static void clear_fifo(struct spi_sh_data *ss) +{ + spi_sh_set_bit(ss, SPI_SH_RSTF, SPI_SH_CR2); + spi_sh_clear_bit(ss, SPI_SH_RSTF, SPI_SH_CR2); +} + +static int spi_sh_wait_receive_buffer(struct spi_sh_data *ss) +{ + int timeout = 100000; + + while (spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_RBE) { + udelay(10); + if (timeout-- < 0) + return -ETIMEDOUT; + } + return 0; +} + +static int spi_sh_wait_write_buffer_empty(struct spi_sh_data *ss) +{ + int timeout = 100000; + + while (!(spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_TBE)) { + udelay(10); + if (timeout-- < 0) + return -ETIMEDOUT; + } + return 0; +} + +static int spi_sh_send(struct spi_sh_data *ss, struct spi_message *mesg, + struct spi_transfer *t) +{ + int i, retval = 0; + int remain = t->len; + int cur_len; + unsigned char *data; + unsigned long tmp; + long ret; + + if (t->len) + spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1); + + data = (unsigned char *)t->tx_buf; + while (remain > 0) { + cur_len = min(SPI_SH_FIFO_SIZE, remain); + for (i = 0; i < cur_len && + !(spi_sh_read(ss, SPI_SH_CR4) & + SPI_SH_WPABRT) && + !(spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_TBF); + i++) + spi_sh_write(ss, (unsigned long)data[i], SPI_SH_TBR); + + if (spi_sh_read(ss, SPI_SH_CR4) & SPI_SH_WPABRT) { + /* Abort SPI operation */ + spi_sh_set_bit(ss, SPI_SH_WPABRT, SPI_SH_CR4); + retval = -EIO; + break; + } + + cur_len = i; + + remain -= cur_len; + data += cur_len; + + if (remain > 0) { + ss->cr1 &= ~SPI_SH_TBE; + spi_sh_set_bit(ss, SPI_SH_TBE, SPI_SH_CR4); + ret = wait_event_interruptible_timeout(ss->wait, + ss->cr1 & SPI_SH_TBE, + SPI_SH_SEND_TIMEOUT); + if (ret == 0 && !(ss->cr1 & SPI_SH_TBE)) { + printk(KERN_ERR "%s: timeout\n", __func__); + return -ETIMEDOUT; + } + } + } + + if (list_is_last(&t->transfer_list, &mesg->transfers)) { + tmp = spi_sh_read(ss, SPI_SH_CR1); + tmp = tmp & ~(SPI_SH_SSD | SPI_SH_SSDB); + spi_sh_write(ss, tmp, SPI_SH_CR1); + spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1); + + ss->cr1 &= ~SPI_SH_TBE; + spi_sh_set_bit(ss, SPI_SH_TBE, SPI_SH_CR4); + ret = wait_event_interruptible_timeout(ss->wait, + ss->cr1 & SPI_SH_TBE, + SPI_SH_SEND_TIMEOUT); + if (ret == 0 && (ss->cr1 & SPI_SH_TBE)) { + printk(KERN_ERR "%s: timeout\n", __func__); + return -ETIMEDOUT; + } + } + + return retval; +} + +static int spi_sh_receive(struct spi_sh_data *ss, struct spi_message *mesg, + struct spi_transfer *t) +{ + int i; + int remain = t->len; + int cur_len; + unsigned char *data; + unsigned long tmp; + long ret; + + if (t->len > SPI_SH_MAX_BYTE) + spi_sh_write(ss, SPI_SH_MAX_BYTE, SPI_SH_CR3); + else + spi_sh_write(ss, t->len, SPI_SH_CR3); + + tmp = spi_sh_read(ss, SPI_SH_CR1); + tmp = tmp & ~(SPI_SH_SSD | SPI_SH_SSDB); + spi_sh_write(ss, tmp, SPI_SH_CR1); + spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1); + + spi_sh_wait_write_buffer_empty(ss); + + data = (unsigned char *)t->rx_buf; + while (remain > 0) { + if (remain >= SPI_SH_FIFO_SIZE) { + ss->cr1 &= ~SPI_SH_RBF; + spi_sh_set_bit(ss, SPI_SH_RBF, SPI_SH_CR4); + ret = wait_event_interruptible_timeout(ss->wait, + ss->cr1 & SPI_SH_RBF, + SPI_SH_RECEIVE_TIMEOUT); + if (ret == 0 && + spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_RBE) { + printk(KERN_ERR "%s: timeout\n", __func__); + return -ETIMEDOUT; + } + } + + cur_len = min(SPI_SH_FIFO_SIZE, remain); + for (i = 0; i < cur_len; i++) { + if (spi_sh_wait_receive_buffer(ss)) + break; + data[i] = (unsigned char)spi_sh_read(ss, SPI_SH_RBR); + } + + remain -= cur_len; + data += cur_len; + } + + /* deassert CS when SPI is receiving. */ + if (t->len > SPI_SH_MAX_BYTE) { + clear_fifo(ss); + spi_sh_write(ss, 1, SPI_SH_CR3); + } else { + spi_sh_write(ss, 0, SPI_SH_CR3); + } + + return 0; +} + +static void spi_sh_work(struct work_struct *work) +{ + struct spi_sh_data *ss = container_of(work, struct spi_sh_data, ws); + struct spi_message *mesg; + struct spi_transfer *t; + unsigned long flags; + int ret; + + pr_debug("%s: enter\n", __func__); + + spin_lock_irqsave(&ss->lock, flags); + while (!list_empty(&ss->queue)) { + mesg = list_entry(ss->queue.next, struct spi_message, queue); + list_del_init(&mesg->queue); + + spin_unlock_irqrestore(&ss->lock, flags); + list_for_each_entry(t, &mesg->transfers, transfer_list) { + pr_debug("tx_buf = %p, rx_buf = %p\n", + t->tx_buf, t->rx_buf); + pr_debug("len = %d, delay_usecs = %d\n", + t->len, t->delay_usecs); + + if (t->tx_buf) { + ret = spi_sh_send(ss, mesg, t); + if (ret < 0) + goto error; + } + if (t->rx_buf) { + ret = spi_sh_receive(ss, mesg, t); + if (ret < 0) + goto error; + } + mesg->actual_length += t->len; + } + spin_lock_irqsave(&ss->lock, flags); + + mesg->status = 0; + mesg->complete(mesg->context); + } + + clear_fifo(ss); + spi_sh_set_bit(ss, SPI_SH_SSD, SPI_SH_CR1); + udelay(100); + + spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD, + SPI_SH_CR1); + + clear_fifo(ss); + + spin_unlock_irqrestore(&ss->lock, flags); + + return; + + error: + mesg->status = ret; + mesg->complete(mesg->context); + + spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD, + SPI_SH_CR1); + clear_fifo(ss); + +} + +static int spi_sh_setup(struct spi_device *spi) +{ + struct spi_sh_data *ss = spi_master_get_devdata(spi->master); + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + pr_debug("%s: enter\n", __func__); + + spi_sh_write(ss, 0xfe, SPI_SH_CR1); /* SPI sycle stop */ + spi_sh_write(ss, 0x00, SPI_SH_CR1); /* CR1 init */ + spi_sh_write(ss, 0x00, SPI_SH_CR3); /* CR3 init */ + + clear_fifo(ss); + + /* 1/8 clock */ + spi_sh_write(ss, spi_sh_read(ss, SPI_SH_CR2) | 0x07, SPI_SH_CR2); + udelay(10); + + return 0; +} + +static int spi_sh_transfer(struct spi_device *spi, struct spi_message *mesg) +{ + struct spi_sh_data *ss = spi_master_get_devdata(spi->master); + unsigned long flags; + + pr_debug("%s: enter\n", __func__); + pr_debug("\tmode = %02x\n", spi->mode); + + spin_lock_irqsave(&ss->lock, flags); + + mesg->actual_length = 0; + mesg->status = -EINPROGRESS; + + spi_sh_clear_bit(ss, SPI_SH_SSA, SPI_SH_CR1); + + list_add_tail(&mesg->queue, &ss->queue); + queue_work(ss->workqueue, &ss->ws); + + spin_unlock_irqrestore(&ss->lock, flags); + + return 0; +} + +static void spi_sh_cleanup(struct spi_device *spi) +{ + struct spi_sh_data *ss = spi_master_get_devdata(spi->master); + + pr_debug("%s: enter\n", __func__); + + spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD, + SPI_SH_CR1); +} + +static irqreturn_t spi_sh_irq(int irq, void *_ss) +{ + struct spi_sh_data *ss = (struct spi_sh_data *)_ss; + unsigned long cr1; + + cr1 = spi_sh_read(ss, SPI_SH_CR1); + if (cr1 & SPI_SH_TBE) + ss->cr1 |= SPI_SH_TBE; + if (cr1 & SPI_SH_TBF) + ss->cr1 |= SPI_SH_TBF; + if (cr1 & SPI_SH_RBE) + ss->cr1 |= SPI_SH_RBE; + if (cr1 & SPI_SH_RBF) + ss->cr1 |= SPI_SH_RBF; + + if (ss->cr1) { + spi_sh_clear_bit(ss, ss->cr1, SPI_SH_CR4); + wake_up(&ss->wait); + } + + return IRQ_HANDLED; +} + +static int __devexit spi_sh_remove(struct platform_device *pdev) +{ + struct spi_sh_data *ss = dev_get_drvdata(&pdev->dev); + + destroy_workqueue(ss->workqueue); + free_irq(ss->irq, ss); + iounmap(ss->addr); + spi_master_put(ss->master); + + return 0; +} + +static int __devinit spi_sh_probe(struct platform_device *pdev) +{ + struct resource *res; + struct spi_master *master; + struct spi_sh_data *ss; + int ret, irq; + + /* get base addr */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(res == NULL)) { + dev_err(&pdev->dev, "invalid resource\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "platform_get_irq error\n"); + return -ENODEV; + } + + master = spi_alloc_master(&pdev->dev, sizeof(struct spi_sh_data)); + if (master == NULL) { + dev_err(&pdev->dev, "spi_alloc_master error.\n"); + return -ENOMEM; + } + + ss = spi_master_get_devdata(master); + dev_set_drvdata(&pdev->dev, ss); + + ss->irq = irq; + ss->master = master; + ss->addr = ioremap(res->start, resource_size(res)); + if (ss->addr == NULL) { + dev_err(&pdev->dev, "ioremap error.\n"); + ret = -ENOMEM; + goto error1; + } + INIT_LIST_HEAD(&ss->queue); + spin_lock_init(&ss->lock); + INIT_WORK(&ss->ws, spi_sh_work); + init_waitqueue_head(&ss->wait); + ss->workqueue = create_singlethread_workqueue( + dev_name(master->dev.parent)); + if (ss->workqueue == NULL) { + dev_err(&pdev->dev, "create workqueue error\n"); + ret = -EBUSY; + goto error2; + } + + ret = request_irq(irq, spi_sh_irq, IRQF_DISABLED, "spi_sh", ss); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq error\n"); + goto error3; + } + + master->num_chipselect = 2; + master->bus_num = pdev->id; + master->setup = spi_sh_setup; + master->transfer = spi_sh_transfer; + master->cleanup = spi_sh_cleanup; + + ret = spi_register_master(master); + if (ret < 0) { + printk(KERN_ERR "spi_register_master error.\n"); + goto error4; + } + + return 0; + + error4: + free_irq(irq, ss); + error3: + destroy_workqueue(ss->workqueue); + error2: + iounmap(ss->addr); + error1: + spi_master_put(master); + + return ret; +} + +static struct platform_driver spi_sh_driver = { + .probe = spi_sh_probe, + .remove = __devexit_p(spi_sh_remove), + .driver = { + .name = "sh_spi", + .owner = THIS_MODULE, + }, +}; + +static int __init spi_sh_init(void) +{ + return platform_driver_register(&spi_sh_driver); +} +module_init(spi_sh_init); + +static void __exit spi_sh_exit(void) +{ + platform_driver_unregister(&spi_sh_driver); +} +module_exit(spi_sh_exit); + +MODULE_DESCRIPTION("SH SPI bus driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yoshihiro Shimoda"); +MODULE_ALIAS("platform:sh_spi"); -- cgit v1.2.2 From c43766707ce26947934ae6bc4497ca5c16bc344f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 16 Feb 2011 09:40:26 +0100 Subject: spi/pl022: rid dangling labels Remove a compilation error regarding unused labels that came about when simplifying the DMA code. Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 4cd05cc05a55..14a451b2ad63 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1030,8 +1030,6 @@ static int configure_dma(struct pl022 *pl022) return 0; -err_submit_tx: -err_submit_rx: err_txdesc: dmaengine_terminate_all(txchan); err_rxdesc: -- cgit v1.2.2 From aa25afad2ca60d19457849ea75e9c31236f4e174 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 19 Feb 2011 15:55:00 +0000 Subject: ARM: amba: make probe() functions take const id tables Make Primecell driver probe functions take a const pointer to their ID tables. Drivers should never modify their ID tables in their probe handler. Signed-off-by: Russell King --- drivers/spi/amba-pl022.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 71a1219a995d..95e58c70a2c9 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -2021,7 +2021,7 @@ static void pl022_cleanup(struct spi_device *spi) static int __devinit -pl022_probe(struct amba_device *adev, struct amba_id *id) +pl022_probe(struct amba_device *adev, const struct amba_id *id) { struct device *dev = &adev->dev; struct pl022_ssp_controller *platform_info = adev->dev.platform_data; -- cgit v1.2.2 From 57d9c10dd91f942f836592f407d6351e2969548a Mon Sep 17 00:00:00 2001 From: Hannu Heikkinen Date: Thu, 24 Feb 2011 21:31:33 +0200 Subject: spi/omap_mcspi: Off-by-one error in finding the right divisor Off-by-one error, gave erroneous divisor value 16 if speed_hz is over zero but less than OMAP2_MCSPI_MAX_FREQ / (1 << 15), that is, [1..1463]. Also few overly complex bit shifts in divisor fixed. Also one dev_dgb line fixed, which indicated max speed exceeding transfer speed. Introducing a new function omap2_mcspi_calc_divisor() for getting the right divisor in omap2_mcspi_setup_transfer(). Signed-off-by: Phil Carmody Signed-off-by: Hannu Heikkinen Signed-off-by: Grant Likely --- drivers/spi/omap2_mcspi.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index abb1ffbf3d20..1a2d0b8bdd6a 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -651,6 +651,17 @@ out: return count - c; } +static u32 omap2_mcspi_calc_divisor(u32 speed_hz) +{ + u32 div; + + for (div = 0; div < 15; div++) + if (speed_hz >= (OMAP2_MCSPI_MAX_FREQ >> div)) + return div; + + return 15; +} + /* called only when no transfer is active to this device */ static int omap2_mcspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) @@ -673,12 +684,8 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, if (t && t->speed_hz) speed_hz = t->speed_hz; - if (speed_hz) { - while (div <= 15 && (OMAP2_MCSPI_MAX_FREQ / (1 << div)) - > speed_hz) - div++; - } else - div = 15; + speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ); + div = omap2_mcspi_calc_divisor(speed_hz); l = mcspi_cached_chconf0(spi); @@ -715,7 +722,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, mcspi_write_chconf0(spi, l); dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n", - OMAP2_MCSPI_MAX_FREQ / (1 << div), + OMAP2_MCSPI_MAX_FREQ >> div, (spi->mode & SPI_CPHA) ? "trailing" : "leading", (spi->mode & SPI_CPOL) ? "inverted" : "normal"); @@ -1015,10 +1022,10 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) t->bits_per_word); return -EINVAL; } - if (t->speed_hz && t->speed_hz < OMAP2_MCSPI_MAX_FREQ/(1<<16)) { - dev_dbg(&spi->dev, "%d Hz max exceeds %d\n", - t->speed_hz, - OMAP2_MCSPI_MAX_FREQ/(1<<16)); + if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) { + dev_dbg(&spi->dev, "speed_hz %d below minimum %d Hz\n", + t->speed_hz, + OMAP2_MCSPI_MAX_FREQ >> 15); return -EINVAL; } -- cgit v1.2.2 From 18d306d1375696b0e6b5b39e4744d7fa2ad5e170 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 22 Feb 2011 21:02:43 -0700 Subject: dt/spi: Eliminate users of of_platform_{,un}register_driver Get rid of users of of_platform_driver in drivers/spi. The of_platform_{,un}register_driver functions are going away, so the users need to be converted to using the platform_bus_type directly. Signed-off-by: Grant Likely --- drivers/spi/mpc512x_psc_spi.c | 9 ++++----- drivers/spi/mpc52xx_psc_spi.c | 9 ++++----- drivers/spi/mpc52xx_spi.c | 9 ++++----- drivers/spi/spi_fsl_espi.c | 11 +++++------ drivers/spi/spi_fsl_lib.c | 3 +-- drivers/spi/spi_fsl_lib.h | 3 +-- drivers/spi/spi_fsl_spi.c | 11 +++++------ drivers/spi/spi_ppc4xx.c | 9 ++++----- 8 files changed, 28 insertions(+), 36 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/mpc512x_psc_spi.c b/drivers/spi/mpc512x_psc_spi.c index 77d9e7ee8b27..6a5b4238fb6b 100644 --- a/drivers/spi/mpc512x_psc_spi.c +++ b/drivers/spi/mpc512x_psc_spi.c @@ -507,8 +507,7 @@ static int __devexit mpc512x_psc_spi_do_remove(struct device *dev) return 0; } -static int __devinit mpc512x_psc_spi_of_probe(struct platform_device *op, - const struct of_device_id *match) +static int __devinit mpc512x_psc_spi_of_probe(struct platform_device *op) { const u32 *regaddr_p; u64 regaddr64, size64; @@ -551,7 +550,7 @@ static struct of_device_id mpc512x_psc_spi_of_match[] = { MODULE_DEVICE_TABLE(of, mpc512x_psc_spi_of_match); -static struct of_platform_driver mpc512x_psc_spi_of_driver = { +static struct platform_driver mpc512x_psc_spi_of_driver = { .probe = mpc512x_psc_spi_of_probe, .remove = __devexit_p(mpc512x_psc_spi_of_remove), .driver = { @@ -563,13 +562,13 @@ static struct of_platform_driver mpc512x_psc_spi_of_driver = { static int __init mpc512x_psc_spi_init(void) { - return of_register_platform_driver(&mpc512x_psc_spi_of_driver); + return platform_driver_register(&mpc512x_psc_spi_of_driver); } module_init(mpc512x_psc_spi_init); static void __exit mpc512x_psc_spi_exit(void) { - of_unregister_platform_driver(&mpc512x_psc_spi_of_driver); + platform_driver_unregister(&mpc512x_psc_spi_of_driver); } module_exit(mpc512x_psc_spi_exit); diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c index 8a904c1c8485..e30baf0852ac 100644 --- a/drivers/spi/mpc52xx_psc_spi.c +++ b/drivers/spi/mpc52xx_psc_spi.c @@ -450,8 +450,7 @@ free_master: return ret; } -static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op, - const struct of_device_id *match) +static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op) { const u32 *regaddr_p; u64 regaddr64, size64; @@ -503,7 +502,7 @@ static const struct of_device_id mpc52xx_psc_spi_of_match[] = { MODULE_DEVICE_TABLE(of, mpc52xx_psc_spi_of_match); -static struct of_platform_driver mpc52xx_psc_spi_of_driver = { +static struct platform_driver mpc52xx_psc_spi_of_driver = { .probe = mpc52xx_psc_spi_of_probe, .remove = __devexit_p(mpc52xx_psc_spi_of_remove), .driver = { @@ -515,13 +514,13 @@ static struct of_platform_driver mpc52xx_psc_spi_of_driver = { static int __init mpc52xx_psc_spi_init(void) { - return of_register_platform_driver(&mpc52xx_psc_spi_of_driver); + return platform_driver_register(&mpc52xx_psc_spi_of_driver); } module_init(mpc52xx_psc_spi_init); static void __exit mpc52xx_psc_spi_exit(void) { - of_unregister_platform_driver(&mpc52xx_psc_spi_of_driver); + platform_driver_unregister(&mpc52xx_psc_spi_of_driver); } module_exit(mpc52xx_psc_spi_exit); diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c index 84439f655601..015a974bed72 100644 --- a/drivers/spi/mpc52xx_spi.c +++ b/drivers/spi/mpc52xx_spi.c @@ -390,8 +390,7 @@ static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m) /* * OF Platform Bus Binding */ -static int __devinit mpc52xx_spi_probe(struct platform_device *op, - const struct of_device_id *match) +static int __devinit mpc52xx_spi_probe(struct platform_device *op) { struct spi_master *master; struct mpc52xx_spi *ms; @@ -556,7 +555,7 @@ static const struct of_device_id mpc52xx_spi_match[] __devinitconst = { }; MODULE_DEVICE_TABLE(of, mpc52xx_spi_match); -static struct of_platform_driver mpc52xx_spi_of_driver = { +static struct platform_driver mpc52xx_spi_of_driver = { .driver = { .name = "mpc52xx-spi", .owner = THIS_MODULE, @@ -568,13 +567,13 @@ static struct of_platform_driver mpc52xx_spi_of_driver = { static int __init mpc52xx_spi_init(void) { - return of_register_platform_driver(&mpc52xx_spi_of_driver); + return platform_driver_register(&mpc52xx_spi_of_driver); } module_init(mpc52xx_spi_init); static void __exit mpc52xx_spi_exit(void) { - of_unregister_platform_driver(&mpc52xx_spi_of_driver); + platform_driver_unregister(&mpc52xx_spi_of_driver); } module_exit(mpc52xx_spi_exit); diff --git a/drivers/spi/spi_fsl_espi.c b/drivers/spi/spi_fsl_espi.c index a99e2333b949..900e921ab80e 100644 --- a/drivers/spi/spi_fsl_espi.c +++ b/drivers/spi/spi_fsl_espi.c @@ -685,8 +685,7 @@ static int of_fsl_espi_get_chipselects(struct device *dev) return 0; } -static int __devinit of_fsl_espi_probe(struct platform_device *ofdev, - const struct of_device_id *ofid) +static int __devinit of_fsl_espi_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; struct device_node *np = ofdev->dev.of_node; @@ -695,7 +694,7 @@ static int __devinit of_fsl_espi_probe(struct platform_device *ofdev, struct resource irq; int ret = -ENOMEM; - ret = of_mpc8xxx_spi_probe(ofdev, ofid); + ret = of_mpc8xxx_spi_probe(ofdev); if (ret) return ret; @@ -736,7 +735,7 @@ static const struct of_device_id of_fsl_espi_match[] = { }; MODULE_DEVICE_TABLE(of, of_fsl_espi_match); -static struct of_platform_driver fsl_espi_driver = { +static struct platform_driver fsl_espi_driver = { .driver = { .name = "fsl_espi", .owner = THIS_MODULE, @@ -748,13 +747,13 @@ static struct of_platform_driver fsl_espi_driver = { static int __init fsl_espi_init(void) { - return of_register_platform_driver(&fsl_espi_driver); + return platform_driver_register(&fsl_espi_driver); } module_init(fsl_espi_init); static void __exit fsl_espi_exit(void) { - of_unregister_platform_driver(&fsl_espi_driver); + platform_driver_unregister(&fsl_espi_driver); } module_exit(fsl_espi_exit); diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi_fsl_lib.c index 5cd741fdb5c3..ff59f42ae990 100644 --- a/drivers/spi/spi_fsl_lib.c +++ b/drivers/spi/spi_fsl_lib.c @@ -189,8 +189,7 @@ int __devexit mpc8xxx_spi_remove(struct device *dev) return 0; } -int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev, - const struct of_device_id *ofid) +int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; struct device_node *np = ofdev->dev.of_node; diff --git a/drivers/spi/spi_fsl_lib.h b/drivers/spi/spi_fsl_lib.h index 281e060977cd..cbe881b9ea76 100644 --- a/drivers/spi/spi_fsl_lib.h +++ b/drivers/spi/spi_fsl_lib.h @@ -118,7 +118,6 @@ extern const char *mpc8xxx_spi_strmode(unsigned int flags); extern int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq); extern int mpc8xxx_spi_remove(struct device *dev); -extern int of_mpc8xxx_spi_probe(struct platform_device *ofdev, - const struct of_device_id *ofid); +extern int of_mpc8xxx_spi_probe(struct platform_device *ofdev); #endif /* __SPI_FSL_LIB_H__ */ diff --git a/drivers/spi/spi_fsl_spi.c b/drivers/spi/spi_fsl_spi.c index 7ca52d3ae8f8..7963c9b49566 100644 --- a/drivers/spi/spi_fsl_spi.c +++ b/drivers/spi/spi_fsl_spi.c @@ -1042,8 +1042,7 @@ static int of_fsl_spi_free_chipselects(struct device *dev) return 0; } -static int __devinit of_fsl_spi_probe(struct platform_device *ofdev, - const struct of_device_id *ofid) +static int __devinit of_fsl_spi_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; struct device_node *np = ofdev->dev.of_node; @@ -1052,7 +1051,7 @@ static int __devinit of_fsl_spi_probe(struct platform_device *ofdev, struct resource irq; int ret = -ENOMEM; - ret = of_mpc8xxx_spi_probe(ofdev, ofid); + ret = of_mpc8xxx_spi_probe(ofdev); if (ret) return ret; @@ -1100,7 +1099,7 @@ static const struct of_device_id of_fsl_spi_match[] = { }; MODULE_DEVICE_TABLE(of, of_fsl_spi_match); -static struct of_platform_driver of_fsl_spi_driver = { +static struct platform_driver of_fsl_spi_driver = { .driver = { .name = "fsl_spi", .owner = THIS_MODULE, @@ -1177,13 +1176,13 @@ static void __exit legacy_driver_unregister(void) {} static int __init fsl_spi_init(void) { legacy_driver_register(); - return of_register_platform_driver(&of_fsl_spi_driver); + return platform_driver_register(&of_fsl_spi_driver); } module_init(fsl_spi_init); static void __exit fsl_spi_exit(void) { - of_unregister_platform_driver(&of_fsl_spi_driver); + platform_driver_unregister(&of_fsl_spi_driver); legacy_driver_unregister(); } module_exit(fsl_spi_exit); diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c index 80e172d3e72a..2a298c029194 100644 --- a/drivers/spi/spi_ppc4xx.c +++ b/drivers/spi/spi_ppc4xx.c @@ -390,8 +390,7 @@ static void free_gpios(struct ppc4xx_spi *hw) /* * platform_device layer stuff... */ -static int __init spi_ppc4xx_of_probe(struct platform_device *op, - const struct of_device_id *match) +static int __init spi_ppc4xx_of_probe(struct platform_device *op) { struct ppc4xx_spi *hw; struct spi_master *master; @@ -586,7 +585,7 @@ static const struct of_device_id spi_ppc4xx_of_match[] = { MODULE_DEVICE_TABLE(of, spi_ppc4xx_of_match); -static struct of_platform_driver spi_ppc4xx_of_driver = { +static struct platform_driver spi_ppc4xx_of_driver = { .probe = spi_ppc4xx_of_probe, .remove = __exit_p(spi_ppc4xx_of_remove), .driver = { @@ -598,13 +597,13 @@ static struct of_platform_driver spi_ppc4xx_of_driver = { static int __init spi_ppc4xx_init(void) { - return of_register_platform_driver(&spi_ppc4xx_of_driver); + return platform_driver_register(&spi_ppc4xx_of_driver); } module_init(spi_ppc4xx_init); static void __exit spi_ppc4xx_exit(void) { - of_unregister_platform_driver(&spi_ppc4xx_of_driver); + platform_driver_unregister(&spi_ppc4xx_of_driver); } module_exit(spi_ppc4xx_exit); -- cgit v1.2.2 From 2e3e2a5e4fef586ae9b1cfef42823c0aef1797f4 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Tue, 8 Feb 2011 07:59:55 -0500 Subject: davinci: spi: move event queue parameter to platform data For DMA operation, the davinci spi driver needs an event queue number. Currently, this number is passed as a IORESOURCE_DMA. This is not correct, as the event queue is not a DMA channel. Pass the event queue via the platform data structure instead. On dm355 and dm365, move the eventq assignment for spi0 out of resources array and into platform data. Signed-off-by: Michael Williamson Acked-by: Sekhar Nori Acked-by: Grant Likely Signed-off-by: Kevin Hilman --- drivers/spi/davinci_spi.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 6beab99bf95b..166a879fd9e8 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -790,7 +790,6 @@ static int davinci_spi_probe(struct platform_device *pdev) struct resource *r, *mem; resource_size_t dma_rx_chan = SPI_NO_RESOURCE; resource_size_t dma_tx_chan = SPI_NO_RESOURCE; - resource_size_t dma_eventq = SPI_NO_RESOURCE; int i = 0, ret = 0; u32 spipc0; @@ -878,17 +877,13 @@ static int davinci_spi_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (r) dma_tx_chan = r->start; - r = platform_get_resource(pdev, IORESOURCE_DMA, 2); - if (r) - dma_eventq = r->start; dspi->bitbang.txrx_bufs = davinci_spi_bufs; if (dma_rx_chan != SPI_NO_RESOURCE && - dma_tx_chan != SPI_NO_RESOURCE && - dma_eventq != SPI_NO_RESOURCE) { + dma_tx_chan != SPI_NO_RESOURCE) { dspi->dma.rx_channel = dma_rx_chan; dspi->dma.tx_channel = dma_tx_chan; - dspi->dma.eventq = dma_eventq; + dspi->dma.eventq = pdata->dma_event_q; ret = davinci_spi_request_dma(dspi); if (ret) @@ -897,7 +892,7 @@ static int davinci_spi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "DMA: supported\n"); dev_info(&pdev->dev, "DMA: RX channel: %d, TX channel: %d, " "event queue: %d\n", dma_rx_chan, dma_tx_chan, - dma_eventq); + pdata->dma_event_q); } dspi->get_rx = davinci_spi_rx_buf_u8; -- cgit v1.2.2 From adef658ddf71e709eb1bdc181b86c62b933b967b Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Fri, 25 Feb 2011 16:55:11 +0100 Subject: spi/omap_mcspi: catch xfers of non-multiple SPI word size If an SPI access was not a multiple of the SPI word size, the while() loop would spin and the rx/tx ptrs would be incremented indefinitely. Signed-off-by: Michael Jones Signed-off-by: Grant Likely --- drivers/spi/omap2_mcspi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index 1a2d0b8bdd6a..6a982a25b7c3 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -487,6 +487,9 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) rx_reg = base + OMAP2_MCSPI_RX0; chstat_reg = base + OMAP2_MCSPI_CHSTAT0; + if (c < (word_len>>3)) + return 0; + if (word_len <= 8) { u8 *rx; const u8 *tx; @@ -534,7 +537,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_vdbg(&spi->dev, "read-%d %02x\n", word_len, *(rx - 1)); } - } while (c); + } while (c > (word_len>>3)); } else if (word_len <= 16) { u16 *rx; const u16 *tx; @@ -581,7 +584,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_vdbg(&spi->dev, "read-%d %04x\n", word_len, *(rx - 1)); } - } while (c); + } while (c > (word_len>>3)); } else if (word_len <= 32) { u32 *rx; const u32 *tx; @@ -628,7 +631,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_vdbg(&spi->dev, "read-%d %08x\n", word_len, *(rx - 1)); } - } while (c); + } while (c > (word_len>>3)); } /* for TX_ONLY mode, be sure all words have shifted out */ -- cgit v1.2.2 From 76851671287209759f63c090ffaffca56ba00358 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Thu, 3 Mar 2011 16:40:02 +0800 Subject: ARM: imx5x: clean up ARCH_MX5X MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move to SOC_SOC_IMX5X. Leave only places which prevent multi-soc using ARCH_MX5X. Signed-off-by: Richard Zhao Acked-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- drivers/spi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bb233a9cbad2..9f9d3f7859fb 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -164,10 +164,10 @@ config SPI_IMX_VER_0_4 def_bool y if ARCH_MX31 config SPI_IMX_VER_0_7 - def_bool y if ARCH_MX25 || ARCH_MX35 || ARCH_MX51 || ARCH_MX53 + def_bool y if ARCH_MX25 || ARCH_MX35 || SOC_IMX51 || SOC_IMX53 config SPI_IMX_VER_2_3 - def_bool y if ARCH_MX51 || ARCH_MX53 + def_bool y if SOC_IMX51 || SOC_IMX53 config SPI_IMX tristate "Freescale i.MX SPI controllers" -- cgit v1.2.2 From c23eb89ef71cda127cfcfee84292cb2fb5747d4d Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Thu, 3 Mar 2011 16:40:03 +0800 Subject: ARM: imx3x: clean up ARCH_MX3X Move to SOC_SOC_IMX3X. Leave ARCH_MX31/35 definitions there, in case some place prevent multi-soc single image. Signed-off-by: Richard Zhao Acked-by: Marc Kleine-Budde Signed-off-by: Sascha Hauer --- drivers/spi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 9f9d3f7859fb..bd9dad5e1b8c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -161,10 +161,10 @@ config SPI_IMX_VER_0_0 def_bool y if SOC_IMX21 || SOC_IMX27 config SPI_IMX_VER_0_4 - def_bool y if ARCH_MX31 + def_bool y if SOC_MX31 config SPI_IMX_VER_0_7 - def_bool y if ARCH_MX25 || ARCH_MX35 || SOC_IMX51 || SOC_IMX53 + def_bool y if ARCH_MX25 || SOC_IMX35 || SOC_IMX51 || SOC_IMX53 config SPI_IMX_VER_2_3 def_bool y if SOC_IMX51 || SOC_IMX53 -- cgit v1.2.2 From beb859645a8a9dfa089bef05206c844d9d34f307 Mon Sep 17 00:00:00 2001 From: Alberto Panizzo Date: Mon, 7 Mar 2011 11:44:13 +0100 Subject: spi_imx: Fix misspelled configuration variable SOC_IMX31 This fix a kernel NULL pointer error while initialising SPI introduced by: 4d2f13be1e370a670c1cae20c194d5ce961e0fa5 Signed-off-by: Alberto Panizzo Signed-off-by: Sascha Hauer --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bd9dad5e1b8c..996cf0359385 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -161,7 +161,7 @@ config SPI_IMX_VER_0_0 def_bool y if SOC_IMX21 || SOC_IMX27 config SPI_IMX_VER_0_4 - def_bool y if SOC_MX31 + def_bool y if SOC_IMX31 config SPI_IMX_VER_0_7 def_bool y if ARCH_MX25 || SOC_IMX35 || SOC_IMX51 || SOC_IMX53 -- cgit v1.2.2 From d09519e41a67eab22cc4566670431f9252b6786a Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 13 Mar 2011 10:34:21 -0400 Subject: spi/davinci: Use correct length parameter to dma_map_single calls The davinci spi driver provides an option to use DMA transfers for data. In the dma_map_single() call, the driver is passing the number of words to be transfered for the mapping size. It should be the number of bytes. Signed-off-by: Michael Williamson Signed-off-by: Grant Likely --- drivers/spi/davinci_spi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 6beab99bf95b..68ed377cc626 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -591,10 +591,10 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) if (t->tx_buf) { t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, - dspi->wcount, DMA_TO_DEVICE); + t->len, DMA_TO_DEVICE); if (dma_mapping_error(&spi->dev, t->tx_dma)) { dev_dbg(sdev, "Unable to DMA map %d bytes" - "TX buffer\n", dspi->wcount); + "TX buffer\n", t->len); return -ENOMEM; } } @@ -624,7 +624,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) if (t->rx_buf) { rx_buf = t->rx_buf; - rx_buf_count = dspi->rcount; + rx_buf_count = t->len; } else { rx_buf = dspi->rx_tmp_buf; rx_buf_count = sizeof(dspi->rx_tmp_buf); @@ -636,7 +636,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", rx_buf_count); if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, dspi->wcount, + dma_unmap_single(NULL, t->tx_dma, t->len, DMA_TO_DEVICE); return -ENOMEM; } @@ -675,7 +675,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) if (spicfg->io_type == SPI_IO_TYPE_DMA) { if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, dspi->wcount, + dma_unmap_single(NULL, t->tx_dma, t->len, DMA_TO_DEVICE); dma_unmap_single(NULL, t->rx_dma, rx_buf_count, -- cgit v1.2.2 From b1178b21c6b9721a07fbd50762067a7c0ffa5a50 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Mon, 14 Mar 2011 11:49:02 -0400 Subject: spi/davinci: Support DMA transfers larger than 65535 words The current davinci SPI driver, in DMA mode, is limited to 65535 words for a single transfer. Modify the driver by configuring a 3 dimensional EDMA transfer to support up to 65535x65535 words. Signed-off-by: Michael Williamson Tested-by: Stefano Babic Signed-off-by: Grant Likely --- drivers/spi/davinci_spi.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 68ed377cc626..e90c2d6a84b3 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -571,6 +571,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) unsigned long tx_reg, rx_reg; struct edmacc_param param; void *rx_buf; + int b, c; dma = &dspi->dma; @@ -599,14 +600,30 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) } } + /* + * If number of words is greater than 65535, then we need + * to configure a 3 dimension transfer. Use the BCNTRLD + * feature to allow for transfers that aren't even multiples + * of 65535 (or any other possible b size) by first transferring + * the remainder amount then grabbing the next N blocks of + * 65535 words. + */ + + c = dspi->wcount / (SZ_64K - 1); /* N 65535 Blocks */ + b = dspi->wcount - c * (SZ_64K - 1); /* Remainder */ + if (b) + c++; + else + b = SZ_64K - 1; + param.opt = TCINTEN | EDMA_TCC(dma->tx_channel); param.src = t->tx_buf ? t->tx_dma : tx_reg; - param.a_b_cnt = dspi->wcount << 16 | data_type; + param.a_b_cnt = b << 16 | data_type; param.dst = tx_reg; param.src_dst_bidx = t->tx_buf ? data_type : 0; - param.link_bcntrld = 0xffff; - param.src_dst_cidx = 0; - param.ccnt = 1; + param.link_bcntrld = 0xffffffff; + param.src_dst_cidx = t->tx_buf ? data_type : 0; + param.ccnt = c; edma_write_slot(dma->tx_channel, ¶m); edma_link(dma->tx_channel, dma->dummy_param_slot); @@ -643,12 +660,12 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) param.opt = TCINTEN | EDMA_TCC(dma->rx_channel); param.src = rx_reg; - param.a_b_cnt = dspi->rcount << 16 | data_type; + param.a_b_cnt = b << 16 | data_type; param.dst = t->rx_dma; param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; - param.link_bcntrld = 0xffff; - param.src_dst_cidx = 0; - param.ccnt = 1; + param.link_bcntrld = 0xffffffff; + param.src_dst_cidx = (t->rx_buf ? data_type : 0) << 16; + param.ccnt = c; edma_write_slot(dma->rx_channel, ¶m); if (pdata->cshold_bug) -- cgit v1.2.2 From a72aeefebe6545ad5c9c699e1121019c1709ca77 Mon Sep 17 00:00:00 2001 From: Cyril Chemparathy Date: Tue, 18 Jan 2011 19:21:36 +0000 Subject: spi: add ti-ssp spi master driver This patch adds an SPI master implementation that operates on top of an underlying TI-SSP port. Acked-by: Grant Likely Signed-off-by: Cyril Chemparathy Signed-off-by: Sekhar Nori Signed-off-by: Kevin Hilman --- drivers/spi/Kconfig | 10 ++ drivers/spi/Makefile | 1 + drivers/spi/ti-ssp-spi.c | 402 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 413 insertions(+) create mode 100644 drivers/spi/ti-ssp-spi.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bb233a9cbad2..42f5a8d7c817 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -350,6 +350,16 @@ config SPI_TEGRA help SPI driver for NVidia Tegra SoCs +config SPI_TI_SSP + tristate "TI Sequencer Serial Port - SPI Support" + depends on MFD_TI_SSP + help + This selects an SPI master implementation using a TI sequencer + serial port. + + To compile this driver as a module, choose M here: the + module will be called ti-ssp-spi. + config SPI_TOPCLIFF_PCH tristate "Topcliff PCH SPI Controller" depends on PCI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 86d1b5f9bbd9..f3f31d988358 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o obj-$(CONFIG_SPI_TEGRA) += spi_tegra.o +obj-$(CONFIG_SPI_TI_SSP) += ti-ssp-spi.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o diff --git a/drivers/spi/ti-ssp-spi.c b/drivers/spi/ti-ssp-spi.c new file mode 100644 index 000000000000..ee22795c7973 --- /dev/null +++ b/drivers/spi/ti-ssp-spi.c @@ -0,0 +1,402 @@ +/* + * Sequencer Serial Port (SSP) based SPI master driver + * + * Copyright (C) 2010 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MODE_BITS (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH) + +struct ti_ssp_spi { + struct spi_master *master; + struct device *dev; + spinlock_t lock; + struct list_head msg_queue; + struct completion complete; + bool shutdown; + struct workqueue_struct *workqueue; + struct work_struct work; + u8 mode, bpw; + int cs_active; + u32 pc_en, pc_dis, pc_wr, pc_rd; + void (*select)(int cs); +}; + +static u32 ti_ssp_spi_rx(struct ti_ssp_spi *hw) +{ + u32 ret; + + ti_ssp_run(hw->dev, hw->pc_rd, 0, &ret); + return ret; +} + +static void ti_ssp_spi_tx(struct ti_ssp_spi *hw, u32 data) +{ + ti_ssp_run(hw->dev, hw->pc_wr, data << (32 - hw->bpw), NULL); +} + +static int ti_ssp_spi_txrx(struct ti_ssp_spi *hw, struct spi_message *msg, + struct spi_transfer *t) +{ + int count; + + if (hw->bpw <= 8) { + u8 *rx = t->rx_buf; + const u8 *tx = t->tx_buf; + + for (count = 0; count < t->len; count += 1) { + if (t->tx_buf) + ti_ssp_spi_tx(hw, *tx++); + if (t->rx_buf) + *rx++ = ti_ssp_spi_rx(hw); + } + } else if (hw->bpw <= 16) { + u16 *rx = t->rx_buf; + const u16 *tx = t->tx_buf; + + for (count = 0; count < t->len; count += 2) { + if (t->tx_buf) + ti_ssp_spi_tx(hw, *tx++); + if (t->rx_buf) + *rx++ = ti_ssp_spi_rx(hw); + } + } else { + u32 *rx = t->rx_buf; + const u32 *tx = t->tx_buf; + + for (count = 0; count < t->len; count += 4) { + if (t->tx_buf) + ti_ssp_spi_tx(hw, *tx++); + if (t->rx_buf) + *rx++ = ti_ssp_spi_rx(hw); + } + } + + msg->actual_length += count; /* bytes transferred */ + + dev_dbg(&msg->spi->dev, "xfer %s%s, %d bytes, %d bpw, count %d%s\n", + t->tx_buf ? "tx" : "", t->rx_buf ? "rx" : "", t->len, + hw->bpw, count, (count < t->len) ? " (under)" : ""); + + return (count < t->len) ? -EIO : 0; /* left over data */ +} + +static void ti_ssp_spi_chip_select(struct ti_ssp_spi *hw, int cs_active) +{ + cs_active = !!cs_active; + if (cs_active == hw->cs_active) + return; + ti_ssp_run(hw->dev, cs_active ? hw->pc_en : hw->pc_dis, 0, NULL); + hw->cs_active = cs_active; +} + +#define __SHIFT_OUT(bits) (SSP_OPCODE_SHIFT | SSP_OUT_MODE | \ + cs_en | clk | SSP_COUNT((bits) * 2 - 1)) +#define __SHIFT_IN(bits) (SSP_OPCODE_SHIFT | SSP_IN_MODE | \ + cs_en | clk | SSP_COUNT((bits) * 2 - 1)) + +static int ti_ssp_spi_setup_transfer(struct ti_ssp_spi *hw, u8 bpw, u8 mode) +{ + int error, idx = 0; + u32 seqram[16]; + u32 cs_en, cs_dis, clk; + u32 topbits, botbits; + + mode &= MODE_BITS; + if (mode == hw->mode && bpw == hw->bpw) + return 0; + + cs_en = (mode & SPI_CS_HIGH) ? SSP_CS_HIGH : SSP_CS_LOW; + cs_dis = (mode & SPI_CS_HIGH) ? SSP_CS_LOW : SSP_CS_HIGH; + clk = (mode & SPI_CPOL) ? SSP_CLK_HIGH : SSP_CLK_LOW; + + /* Construct instructions */ + + /* Disable Chip Select */ + hw->pc_dis = idx; + seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_dis | clk; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_dis | clk; + + /* Enable Chip Select */ + hw->pc_en = idx; + seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_en | clk; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; + + /* Reads and writes need to be split for bpw > 16 */ + topbits = (bpw > 16) ? 16 : bpw; + botbits = bpw - topbits; + + /* Write */ + hw->pc_wr = idx; + seqram[idx++] = __SHIFT_OUT(topbits) | SSP_ADDR_REG; + if (botbits) + seqram[idx++] = __SHIFT_OUT(botbits) | SSP_DATA_REG; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; + + /* Read */ + hw->pc_rd = idx; + if (botbits) + seqram[idx++] = __SHIFT_IN(botbits) | SSP_ADDR_REG; + seqram[idx++] = __SHIFT_IN(topbits) | SSP_DATA_REG; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; + + error = ti_ssp_load(hw->dev, 0, seqram, idx); + if (error < 0) + return error; + + error = ti_ssp_set_mode(hw->dev, ((mode & SPI_CPHA) ? + 0 : SSP_EARLY_DIN)); + if (error < 0) + return error; + + hw->bpw = bpw; + hw->mode = mode; + + return error; +} + +static void ti_ssp_spi_work(struct work_struct *work) +{ + struct ti_ssp_spi *hw = container_of(work, struct ti_ssp_spi, work); + + spin_lock(&hw->lock); + + while (!list_empty(&hw->msg_queue)) { + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + int status = 0; + + m = container_of(hw->msg_queue.next, struct spi_message, + queue); + + list_del_init(&m->queue); + + spin_unlock(&hw->lock); + + spi = m->spi; + + if (hw->select) + hw->select(spi->chip_select); + + list_for_each_entry(t, &m->transfers, transfer_list) { + int bpw = spi->bits_per_word; + int xfer_status; + + if (t->bits_per_word) + bpw = t->bits_per_word; + + if (ti_ssp_spi_setup_transfer(hw, bpw, spi->mode) < 0) + break; + + ti_ssp_spi_chip_select(hw, 1); + + xfer_status = ti_ssp_spi_txrx(hw, m, t); + if (xfer_status < 0) + status = xfer_status; + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (t->cs_change) + ti_ssp_spi_chip_select(hw, 0); + } + + ti_ssp_spi_chip_select(hw, 0); + m->status = status; + m->complete(m->context); + + spin_lock(&hw->lock); + } + + if (hw->shutdown) + complete(&hw->complete); + + spin_unlock(&hw->lock); +} + +static int ti_ssp_spi_setup(struct spi_device *spi) +{ + if (spi->bits_per_word > 32) + return -EINVAL; + + return 0; +} + +static int ti_ssp_spi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct ti_ssp_spi *hw; + struct spi_transfer *t; + int error = 0; + + m->actual_length = 0; + m->status = -EINPROGRESS; + + hw = spi_master_get_devdata(spi->master); + + if (list_empty(&m->transfers) || !m->complete) + return -EINVAL; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->len && !(t->rx_buf || t->tx_buf)) { + dev_err(&spi->dev, "invalid xfer, no buffer\n"); + return -EINVAL; + } + + if (t->len && t->rx_buf && t->tx_buf) { + dev_err(&spi->dev, "invalid xfer, full duplex\n"); + return -EINVAL; + } + + if (t->bits_per_word > 32) { + dev_err(&spi->dev, "invalid xfer width %d\n", + t->bits_per_word); + return -EINVAL; + } + } + + spin_lock(&hw->lock); + if (hw->shutdown) { + error = -ESHUTDOWN; + goto error_unlock; + } + list_add_tail(&m->queue, &hw->msg_queue); + queue_work(hw->workqueue, &hw->work); +error_unlock: + spin_unlock(&hw->lock); + return error; +} + +static int __devinit ti_ssp_spi_probe(struct platform_device *pdev) +{ + const struct ti_ssp_spi_data *pdata; + struct ti_ssp_spi *hw; + struct spi_master *master; + struct device *dev = &pdev->dev; + int error = 0; + + pdata = dev->platform_data; + if (!pdata) { + dev_err(dev, "platform data not found\n"); + return -EINVAL; + } + + master = spi_alloc_master(dev, sizeof(struct ti_ssp_spi)); + if (!master) { + dev_err(dev, "cannot allocate SPI master\n"); + return -ENOMEM; + } + + hw = spi_master_get_devdata(master); + platform_set_drvdata(pdev, hw); + + hw->master = master; + hw->dev = dev; + hw->select = pdata->select; + + spin_lock_init(&hw->lock); + init_completion(&hw->complete); + INIT_LIST_HEAD(&hw->msg_queue); + INIT_WORK(&hw->work, ti_ssp_spi_work); + + hw->workqueue = create_singlethread_workqueue(dev_name(dev)); + if (!hw->workqueue) { + error = -ENOMEM; + dev_err(dev, "work queue creation failed\n"); + goto error_wq; + } + + error = ti_ssp_set_iosel(hw->dev, pdata->iosel); + if (error < 0) { + dev_err(dev, "io setup failed\n"); + goto error_iosel; + } + + master->bus_num = pdev->id; + master->num_chipselect = pdata->num_cs; + master->mode_bits = MODE_BITS; + master->flags = SPI_MASTER_HALF_DUPLEX; + master->setup = ti_ssp_spi_setup; + master->transfer = ti_ssp_spi_transfer; + + error = spi_register_master(master); + if (error) { + dev_err(dev, "master registration failed\n"); + goto error_reg; + } + + return 0; + +error_reg: +error_iosel: + destroy_workqueue(hw->workqueue); +error_wq: + spi_master_put(master); + return error; +} + +static int __devexit ti_ssp_spi_remove(struct platform_device *pdev) +{ + struct ti_ssp_spi *hw = platform_get_drvdata(pdev); + int error; + + hw->shutdown = 1; + while (!list_empty(&hw->msg_queue)) { + error = wait_for_completion_interruptible(&hw->complete); + if (error < 0) { + hw->shutdown = 0; + return error; + } + } + destroy_workqueue(hw->workqueue); + spi_unregister_master(hw->master); + + return 0; +} + +static struct platform_driver ti_ssp_spi_driver = { + .probe = ti_ssp_spi_probe, + .remove = __devexit_p(ti_ssp_spi_remove), + .driver = { + .name = "ti-ssp-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init ti_ssp_spi_init(void) +{ + return platform_driver_register(&ti_ssp_spi_driver); +} +module_init(ti_ssp_spi_init); + +static void __exit ti_ssp_spi_exit(void) +{ + platform_driver_unregister(&ti_ssp_spi_driver); +} +module_exit(ti_ssp_spi_exit); + +MODULE_DESCRIPTION("SSP SPI Master"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ti-ssp-spi"); -- cgit v1.2.2 From 568a60eda2e90a11bb3d7f8ef3f6800e9b60d4e5 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 28 Feb 2011 12:47:12 -0700 Subject: spi/dw_spi: move dw_spi.h into drivers/spi include/linux/dw_spi.h only includes driver internal data. It doesn't expose a platform_data configuration structure or similar (at least nothing in-tree). This patch moves the header into drivers/spi so that the scope is limited to only the dw_spi_*.c driver files Signed-off-by: Grant Likely Cc: Feng Tang Cc: spi-devel-general@lists.sourceforge.net --- drivers/spi/dw_spi.c | 4 +- drivers/spi/dw_spi.h | 234 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/dw_spi_mid.c | 3 +- drivers/spi/dw_spi_mmio.c | 4 +- drivers/spi/dw_spi_pci.c | 3 +- 5 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 drivers/spi/dw_spi.h (limited to 'drivers/spi') diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 22af77f98816..9a6196461b27 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -22,10 +22,10 @@ #include #include #include - -#include #include +#include "dw_spi.h" + #ifdef CONFIG_DEBUG_FS #include #endif diff --git a/drivers/spi/dw_spi.h b/drivers/spi/dw_spi.h new file mode 100644 index 000000000000..fb0bce564844 --- /dev/null +++ b/drivers/spi/dw_spi.h @@ -0,0 +1,234 @@ +#ifndef DW_SPI_HEADER_H +#define DW_SPI_HEADER_H + +#include +#include + +/* Bit fields in CTRLR0 */ +#define SPI_DFS_OFFSET 0 + +#define SPI_FRF_OFFSET 4 +#define SPI_FRF_SPI 0x0 +#define SPI_FRF_SSP 0x1 +#define SPI_FRF_MICROWIRE 0x2 +#define SPI_FRF_RESV 0x3 + +#define SPI_MODE_OFFSET 6 +#define SPI_SCPH_OFFSET 6 +#define SPI_SCOL_OFFSET 7 + +#define SPI_TMOD_OFFSET 8 +#define SPI_TMOD_MASK (0x3 << SPI_TMOD_OFFSET) +#define SPI_TMOD_TR 0x0 /* xmit & recv */ +#define SPI_TMOD_TO 0x1 /* xmit only */ +#define SPI_TMOD_RO 0x2 /* recv only */ +#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ + +#define SPI_SLVOE_OFFSET 10 +#define SPI_SRL_OFFSET 11 +#define SPI_CFS_OFFSET 12 + +/* Bit fields in SR, 7 bits */ +#define SR_MASK 0x7f /* cover 7 bits */ +#define SR_BUSY (1 << 0) +#define SR_TF_NOT_FULL (1 << 1) +#define SR_TF_EMPT (1 << 2) +#define SR_RF_NOT_EMPT (1 << 3) +#define SR_RF_FULL (1 << 4) +#define SR_TX_ERR (1 << 5) +#define SR_DCOL (1 << 6) + +/* Bit fields in ISR, IMR, RISR, 7 bits */ +#define SPI_INT_TXEI (1 << 0) +#define SPI_INT_TXOI (1 << 1) +#define SPI_INT_RXUI (1 << 2) +#define SPI_INT_RXOI (1 << 3) +#define SPI_INT_RXFI (1 << 4) +#define SPI_INT_MSTI (1 << 5) + +/* TX RX interrupt level threshhold, max can be 256 */ +#define SPI_INT_THRESHOLD 32 + +enum dw_ssi_type { + SSI_MOTO_SPI = 0, + SSI_TI_SSP, + SSI_NS_MICROWIRE, +}; + +struct dw_spi_reg { + u32 ctrl0; + u32 ctrl1; + u32 ssienr; + u32 mwcr; + u32 ser; + u32 baudr; + u32 txfltr; + u32 rxfltr; + u32 txflr; + u32 rxflr; + u32 sr; + u32 imr; + u32 isr; + u32 risr; + u32 txoicr; + u32 rxoicr; + u32 rxuicr; + u32 msticr; + u32 icr; + u32 dmacr; + u32 dmatdlr; + u32 dmardlr; + u32 idr; + u32 version; + u32 dr; /* Currently oper as 32 bits, + though only low 16 bits matters */ +} __packed; + +struct dw_spi; +struct dw_spi_dma_ops { + int (*dma_init)(struct dw_spi *dws); + void (*dma_exit)(struct dw_spi *dws); + int (*dma_transfer)(struct dw_spi *dws, int cs_change); +}; + +struct dw_spi { + struct spi_master *master; + struct spi_device *cur_dev; + struct device *parent_dev; + enum dw_ssi_type type; + + void __iomem *regs; + unsigned long paddr; + u32 iolen; + int irq; + u32 fifo_len; /* depth of the FIFO buffer */ + u32 max_freq; /* max bus freq supported */ + + u16 bus_num; + u16 num_cs; /* supported slave numbers */ + + /* Driver message queue */ + struct workqueue_struct *workqueue; + struct work_struct pump_messages; + spinlock_t lock; + struct list_head queue; + int busy; + int run; + + /* Message Transfer pump */ + struct tasklet_struct pump_transfers; + + /* Current message transfer state info */ + struct spi_message *cur_msg; + struct spi_transfer *cur_transfer; + struct chip_data *cur_chip; + struct chip_data *prev_chip; + size_t len; + void *tx; + void *tx_end; + void *rx; + void *rx_end; + int dma_mapped; + dma_addr_t rx_dma; + dma_addr_t tx_dma; + size_t rx_map_len; + size_t tx_map_len; + u8 n_bytes; /* current is a 1/2 bytes op */ + u8 max_bits_per_word; /* maxim is 16b */ + u32 dma_width; + int cs_change; + int (*write)(struct dw_spi *dws); + int (*read)(struct dw_spi *dws); + irqreturn_t (*transfer_handler)(struct dw_spi *dws); + void (*cs_control)(u32 command); + + /* Dma info */ + int dma_inited; + struct dma_chan *txchan; + struct scatterlist tx_sgl; + struct dma_chan *rxchan; + struct scatterlist rx_sgl; + int dma_chan_done; + struct device *dma_dev; + dma_addr_t dma_addr; /* phy address of the Data register */ + struct dw_spi_dma_ops *dma_ops; + void *dma_priv; /* platform relate info */ + struct pci_dev *dmac; + + /* Bus interface info */ + void *priv; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs; +#endif +}; + +#define dw_readl(dw, name) \ + __raw_readl(&(((struct dw_spi_reg *)dw->regs)->name)) +#define dw_writel(dw, name, val) \ + __raw_writel((val), &(((struct dw_spi_reg *)dw->regs)->name)) +#define dw_readw(dw, name) \ + __raw_readw(&(((struct dw_spi_reg *)dw->regs)->name)) +#define dw_writew(dw, name, val) \ + __raw_writew((val), &(((struct dw_spi_reg *)dw->regs)->name)) + +static inline void spi_enable_chip(struct dw_spi *dws, int enable) +{ + dw_writel(dws, ssienr, (enable ? 1 : 0)); +} + +static inline void spi_set_clk(struct dw_spi *dws, u16 div) +{ + dw_writel(dws, baudr, div); +} + +static inline void spi_chip_sel(struct dw_spi *dws, u16 cs) +{ + if (cs > dws->num_cs) + return; + + if (dws->cs_control) + dws->cs_control(1); + + dw_writel(dws, ser, 1 << cs); +} + +/* Disable IRQ bits */ +static inline void spi_mask_intr(struct dw_spi *dws, u32 mask) +{ + u32 new_mask; + + new_mask = dw_readl(dws, imr) & ~mask; + dw_writel(dws, imr, new_mask); +} + +/* Enable IRQ bits */ +static inline void spi_umask_intr(struct dw_spi *dws, u32 mask) +{ + u32 new_mask; + + new_mask = dw_readl(dws, imr) | mask; + dw_writel(dws, imr, new_mask); +} + +/* + * Each SPI slave device to work with dw_api controller should + * has such a structure claiming its working mode (PIO/DMA etc), + * which can be save in the "controller_data" member of the + * struct spi_device + */ +struct dw_spi_chip { + u8 poll_mode; /* 0 for contoller polling mode */ + u8 type; /* SPI/SSP/Micrwire */ + u8 enable_dma; + void (*cs_control)(u32 command); +}; + +extern int dw_spi_add_host(struct dw_spi *dws); +extern void dw_spi_remove_host(struct dw_spi *dws); +extern int dw_spi_suspend_host(struct dw_spi *dws); +extern int dw_spi_resume_host(struct dw_spi *dws); +extern void dw_spi_xfer_done(struct dw_spi *dws); + +/* platform related setup */ +extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */ +#endif /* DW_SPI_HEADER_H */ diff --git a/drivers/spi/dw_spi_mid.c b/drivers/spi/dw_spi_mid.c index c91c966e0717..489178243d88 100644 --- a/drivers/spi/dw_spi_mid.c +++ b/drivers/spi/dw_spi_mid.c @@ -22,7 +22,8 @@ #include #include #include -#include + +#include "dw_spi.h" #ifdef CONFIG_SPI_DW_MID_DMA #include diff --git a/drivers/spi/dw_spi_mmio.c b/drivers/spi/dw_spi_mmio.c index 2fa012c109bc..e0e813dad150 100644 --- a/drivers/spi/dw_spi_mmio.c +++ b/drivers/spi/dw_spi_mmio.c @@ -13,8 +13,10 @@ #include #include #include -#include #include +#include + +#include "dw_spi.h" #define DRIVER_NAME "dw_spi_mmio" diff --git a/drivers/spi/dw_spi_pci.c b/drivers/spi/dw_spi_pci.c index 49ec3aa1219f..ad260aa5e526 100644 --- a/drivers/spi/dw_spi_pci.c +++ b/drivers/spi/dw_spi_pci.c @@ -20,9 +20,10 @@ #include #include #include -#include #include +#include "dw_spi.h" + #define DRIVER_NAME "dw_spi_pci" struct dw_spi_pci { -- cgit v1.2.2 From e46dccff341068d8530610a822965794f70b998f Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Thu, 17 Feb 2011 19:07:15 -0800 Subject: mfd: mfd_cell is now implicitly available to timberdale drivers The cell's platform_data is now accessed with a helper function; change clients to use that, and remove the now-unused data_size. Note that the mfd's platform_data is marked __devinitdata. This is still correct in all cases except for the timbgpio driver, whose remove hook has been changed to no longer reference the pdata. Signed-off-by: Andres Salomon Signed-off-by: Samuel Ortiz --- drivers/spi/xilinx_spi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 4d2c75df886c..c69c6f2c2c5c 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -470,7 +471,7 @@ static int __devinit xilinx_spi_probe(struct platform_device *dev) struct spi_master *master; u8 i; - pdata = dev->dev.platform_data; + pdata = mfd_get_data(dev); if (pdata) { num_cs = pdata->num_chipselect; little_endian = pdata->little_endian; -- cgit v1.2.2 From 95c5c3ab7db0dcaeebeb771b90152cd47aa27243 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 21 Mar 2011 16:27:30 +0200 Subject: spi/omap_mcspi: Fix broken last word xfer Commit adef658 "spi/omap_mcspi: catch xfers of non-multiple SPI word size" broke the transmission of last word in cases where access is multiple of word size and word size is 16 or 32 bits. Fix this by replacing the test "c > (word_len>>3)" in do-while loops with "c >= 'pointer increment size'". This ensures that the last word is transmitted in above case and still allow to break the loop and prevent variable c underflow in cases where word size != 'pointer increment size'. Signed-off-by: Jarkko Nikula Tested-by: Sourav Poddar Acked-by: Michael Jones Signed-off-by: Grant Likely --- drivers/spi/omap2_mcspi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index 3a5ed06d3d2f..6f86ba0175ac 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -517,7 +517,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_vdbg(&spi->dev, "read-%d %02x\n", word_len, *(rx - 1)); } - } while (c > (word_len>>3)); + } while (c); } else if (word_len <= 16) { u16 *rx; const u16 *tx; @@ -564,7 +564,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_vdbg(&spi->dev, "read-%d %04x\n", word_len, *(rx - 1)); } - } while (c > (word_len>>3)); + } while (c >= 2); } else if (word_len <= 32) { u32 *rx; const u32 *tx; @@ -611,7 +611,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_vdbg(&spi->dev, "read-%d %08x\n", word_len, *(rx - 1)); } - } while (c > (word_len>>3)); + } while (c >= 4); } /* for TX_ONLY mode, be sure all words have shifted out */ -- cgit v1.2.2 From 06fb01fd1dc624d891cbe039a88a44bc8a8a9ec1 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 23 Mar 2011 11:05:16 +0100 Subject: spi/pl022: Add loopback support for the SPI on 5500 Extend the vendor data with a loopback field, and add new amba-pl022 vendor data for the DB5500 pl023, as the pl023 on db8500 and db5500 vary. Signed-off-by: Prajadevi H Signed-off-by: Philippe Langlais Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 5c2b092a915e..5a4e0afb9ad6 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -324,6 +324,7 @@ struct vendor_data { bool unidir; bool extended_cr; bool pl023; + bool loopback; }; /** @@ -1983,7 +1984,7 @@ static int pl022_setup(struct spi_device *spi) SSP_WRITE_BITS(chip->cr0, clk_freq.scr, SSP_CR0_MASK_SCR, 8); /* Loopback is available on all versions except PL023 */ - if (!pl022->vendor->pl023) { + if (pl022->vendor->loopback) { if (spi->mode & SPI_LOOP) tmp = LOOPBACK_ENABLED; else @@ -2233,6 +2234,7 @@ static struct vendor_data vendor_arm = { .unidir = false, .extended_cr = false, .pl023 = false, + .loopback = true, }; @@ -2242,6 +2244,7 @@ static struct vendor_data vendor_st = { .unidir = false, .extended_cr = true, .pl023 = false, + .loopback = true, }; static struct vendor_data vendor_st_pl023 = { @@ -2250,6 +2253,16 @@ static struct vendor_data vendor_st_pl023 = { .unidir = false, .extended_cr = true, .pl023 = true, + .loopback = false, +}; + +static struct vendor_data vendor_db5500_pl023 = { + .fifodepth = 32, + .max_bpw = 32, + .unidir = false, + .extended_cr = true, + .pl023 = true, + .loopback = true, }; static struct amba_id pl022_ids[] = { @@ -2283,6 +2296,11 @@ static struct amba_id pl022_ids[] = { .mask = 0xffffffff, .data = &vendor_st_pl023, }, + { + .id = 0x10080023, + .mask = 0xffffffff, + .data = &vendor_db5500_pl023, + }, { 0, 0 }, }; -- cgit v1.2.2 From de6efe0a966cf86b3c4039a610b2d4157db707f2 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Wed, 30 Mar 2011 23:09:52 +0800 Subject: spi/dw_spi: unify the low level read/write routines The original version has many duplicated codes for null/u8/u16 case, so unify them to make it cleaner Signed-off-by: Feng Tang Signed-off-by: Grant Likely --- drivers/spi/dw_spi.c | 103 +++++++++++++-------------------------------------- drivers/spi/dw_spi.h | 2 - 2 files changed, 26 insertions(+), 79 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 9a6196461b27..c4fca3d9d45f 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -58,8 +58,6 @@ struct chip_data { u8 bits_per_word; u16 clk_div; /* baud rate divider */ u32 speed_hz; /* baud rate */ - int (*write)(struct dw_spi *dws); - int (*read)(struct dw_spi *dws); void (*cs_control)(u32 command); }; @@ -185,80 +183,45 @@ static void flush(struct dw_spi *dws) wait_till_not_busy(dws); } -static int null_writer(struct dw_spi *dws) -{ - u8 n_bytes = dws->n_bytes; - - if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL) - || (dws->tx == dws->tx_end)) - return 0; - dw_writew(dws, dr, 0); - dws->tx += n_bytes; - wait_till_not_busy(dws); - return 1; -} - -static int null_reader(struct dw_spi *dws) +static int dw_writer(struct dw_spi *dws) { - u8 n_bytes = dws->n_bytes; + u16 txw = 0; - while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT) - && (dws->rx < dws->rx_end)) { - dw_readw(dws, dr); - dws->rx += n_bytes; - } - wait_till_not_busy(dws); - return dws->rx == dws->rx_end; -} - -static int u8_writer(struct dw_spi *dws) -{ if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL) || (dws->tx == dws->tx_end)) return 0; - dw_writew(dws, dr, *(u8 *)(dws->tx)); - ++dws->tx; - - wait_till_not_busy(dws); - return 1; -} - -static int u8_reader(struct dw_spi *dws) -{ - while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT) - && (dws->rx < dws->rx_end)) { - *(u8 *)(dws->rx) = dw_readw(dws, dr); - ++dws->rx; + /* Set the tx word if the transfer's original "tx" is not null */ + if (dws->tx_end - dws->len) { + if (dws->n_bytes == 1) + txw = *(u8 *)(dws->tx); + else + txw = *(u16 *)(dws->tx); } - wait_till_not_busy(dws); - return dws->rx == dws->rx_end; -} - -static int u16_writer(struct dw_spi *dws) -{ - if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL) - || (dws->tx == dws->tx_end)) - return 0; - - dw_writew(dws, dr, *(u16 *)(dws->tx)); - dws->tx += 2; + dw_writew(dws, dr, txw); + dws->tx += dws->n_bytes; wait_till_not_busy(dws); return 1; } -static int u16_reader(struct dw_spi *dws) +static int dw_reader(struct dw_spi *dws) { - u16 temp; + u16 rxw; while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT) && (dws->rx < dws->rx_end)) { - temp = dw_readw(dws, dr); - *(u16 *)(dws->rx) = temp; - dws->rx += 2; + rxw = dw_readw(dws, dr); + /* Care rx only if the transfer's original "rx" is not null */ + if (dws->rx_end - dws->len) { + if (dws->n_bytes == 1) + *(u8 *)(dws->rx) = rxw; + else + *(u16 *)(dws->rx) = rxw; + } + dws->rx += dws->n_bytes; } wait_till_not_busy(dws); @@ -383,8 +346,8 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) left = (left > int_level) ? int_level : left; while (left--) - dws->write(dws); - dws->read(dws); + dw_writer(dws); + dw_reader(dws); /* Re-enable the IRQ if there is still data left to tx */ if (dws->tx_end > dws->tx) @@ -417,13 +380,13 @@ 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) { - while (dws->write(dws)) - dws->read(dws); + while (dw_writer(dws)) + dw_reader(dws); /* * There is a possibility that the last word of a transaction * will be lost if data is not ready. Re-read to solve this issue. */ - dws->read(dws); + dw_reader(dws); dw_spi_xfer_done(dws); } @@ -483,8 +446,6 @@ static void pump_transfers(unsigned long data) dws->tx_end = dws->tx + transfer->len; dws->rx = transfer->rx_buf; dws->rx_end = dws->rx + transfer->len; - dws->write = dws->tx ? chip->write : null_writer; - dws->read = dws->rx ? chip->read : null_reader; dws->cs_change = transfer->cs_change; dws->len = dws->cur_transfer->len; if (chip != dws->prev_chip) @@ -520,18 +481,10 @@ static void pump_transfers(unsigned long data) case 8: dws->n_bytes = 1; dws->dma_width = 1; - dws->read = (dws->read != null_reader) ? - u8_reader : null_reader; - dws->write = (dws->write != null_writer) ? - u8_writer : null_writer; break; case 16: dws->n_bytes = 2; dws->dma_width = 2; - dws->read = (dws->read != null_reader) ? - u16_reader : null_reader; - dws->write = (dws->write != null_writer) ? - u16_writer : null_writer; break; default: printk(KERN_ERR "MRST SPI0: unsupported bits:" @@ -733,13 +686,9 @@ static int dw_spi_setup(struct spi_device *spi) if (spi->bits_per_word <= 8) { chip->n_bytes = 1; chip->dma_width = 1; - chip->read = u8_reader; - chip->write = u8_writer; } else if (spi->bits_per_word <= 16) { chip->n_bytes = 2; chip->dma_width = 2; - chip->read = u16_reader; - chip->write = u16_writer; } else { /* Never take >16b case for MRST SPIC */ dev_err(&spi->dev, "invalid wordsize\n"); diff --git a/drivers/spi/dw_spi.h b/drivers/spi/dw_spi.h index fb0bce564844..d8aac1f4c546 100644 --- a/drivers/spi/dw_spi.h +++ b/drivers/spi/dw_spi.h @@ -137,8 +137,6 @@ struct dw_spi { u8 max_bits_per_word; /* maxim is 16b */ u32 dma_width; int cs_change; - int (*write)(struct dw_spi *dws); - int (*read)(struct dw_spi *dws); irqreturn_t (*transfer_handler)(struct dw_spi *dws); void (*cs_control)(u32 command); -- cgit v1.2.2 From 8a33a373e5ffb6040c58ff41ea48ba21d5f8b5e9 Mon Sep 17 00:00:00 2001 From: Alek Du Date: Wed, 30 Mar 2011 23:09:53 +0800 Subject: spi/dw_spi: remove the un-necessary flush() The flush() is used to drain all the left data in rx fifo, currently is is always called together with disabling hw. But from spec, disabling hw will also reset all the fifo, so flush() is not needed. Signed-off-by: Alek Du Signed-off-by: Feng Tang Signed-off-by: Grant Likely --- drivers/spi/dw_spi.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index c4fca3d9d45f..d3aaf8db86cc 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -173,17 +173,6 @@ static void wait_till_not_busy(struct dw_spi *dws) "DW SPI: Status keeps busy for 5000us after a read/write!\n"); } -static void flush(struct dw_spi *dws) -{ - while (dw_readw(dws, sr) & SR_RF_NOT_EMPT) { - dw_readw(dws, dr); - cpu_relax(); - } - - wait_till_not_busy(dws); -} - - static int dw_writer(struct dw_spi *dws) { u16 txw = 0; @@ -297,8 +286,7 @@ static void giveback(struct dw_spi *dws) static void int_error_stop(struct dw_spi *dws, const char *msg) { - /* Stop and reset hw */ - flush(dws); + /* Stop the hw */ spi_enable_chip(dws, 0); dev_err(&dws->master->dev, "%s\n", msg); @@ -800,7 +788,6 @@ static void spi_hw_init(struct dw_spi *dws) spi_enable_chip(dws, 0); spi_mask_intr(dws, 0xff); spi_enable_chip(dws, 1); - flush(dws); /* * Try to detect the FIFO depth if not set by interface driver, -- cgit v1.2.2 From 2ff271bf6505038d8c937e73438ea3c80c387439 Mon Sep 17 00:00:00 2001 From: Alek Du Date: Wed, 30 Mar 2011 23:09:54 +0800 Subject: spi/dw_spi: change poll mode transfer from byte ops to batch ops Current poll transfer will read/write one word, then wait till the hw is non-busy, it's not efficient. This patch will try to read/write as many words as permitted by hardware FIFO depth. Signed-off-by: Alek Du Signed-off-by: Feng Tang Signed-off-by: Grant Likely --- drivers/spi/dw_spi.c | 71 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 23 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index d3aaf8db86cc..7a2a72268f0a 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -160,6 +160,37 @@ static inline void mrst_spi_debugfs_remove(struct dw_spi *dws) } #endif /* CONFIG_DEBUG_FS */ +/* Return the max entries we can fill into tx fifo */ +static inline u32 tx_max(struct dw_spi *dws) +{ + u32 tx_left, tx_room, rxtx_gap; + + tx_left = (dws->tx_end - dws->tx) / dws->n_bytes; + tx_room = dws->fifo_len - dw_readw(dws, txflr); + + /* + * Another concern is about the tx/rx mismatch, we + * though to use (dws->fifo_len - rxflr - txflr) as + * one maximum value for tx, but it doesn't cover the + * data which is out of tx/rx fifo and inside the + * shift registers. So a control from sw point of + * view is taken. + */ + rxtx_gap = ((dws->rx_end - dws->rx) - (dws->tx_end - dws->tx)) + / dws->n_bytes; + + return min3(tx_left, tx_room, (u32) (dws->fifo_len - rxtx_gap)); +} + +/* Return the max entries we should read out of rx fifo */ +static inline u32 rx_max(struct dw_spi *dws) +{ + u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes; + + return min(rx_left, (u32)dw_readw(dws, rxflr)); +} + + static void wait_till_not_busy(struct dw_spi *dws) { unsigned long end = jiffies + 1 + usecs_to_jiffies(5000); @@ -175,33 +206,30 @@ static void wait_till_not_busy(struct dw_spi *dws) static int dw_writer(struct dw_spi *dws) { + u32 max = tx_max(dws); u16 txw = 0; - if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL) - || (dws->tx == dws->tx_end)) - return 0; - - /* Set the tx word if the transfer's original "tx" is not null */ - if (dws->tx_end - dws->len) { - if (dws->n_bytes == 1) - txw = *(u8 *)(dws->tx); - else - txw = *(u16 *)(dws->tx); + while (max--) { + /* Set the tx word if the transfer's original "tx" is not null */ + if (dws->tx_end - dws->len) { + if (dws->n_bytes == 1) + txw = *(u8 *)(dws->tx); + else + txw = *(u16 *)(dws->tx); + } + dw_writew(dws, dr, txw); + dws->tx += dws->n_bytes; } - dw_writew(dws, dr, txw); - dws->tx += dws->n_bytes; - - wait_till_not_busy(dws); return 1; } static int dw_reader(struct dw_spi *dws) { + u32 max = rx_max(dws); u16 rxw; - while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT) - && (dws->rx < dws->rx_end)) { + while (max--) { rxw = dw_readw(dws, dr); /* Care rx only if the transfer's original "rx" is not null */ if (dws->rx_end - dws->len) { @@ -213,7 +241,6 @@ static int dw_reader(struct dw_spi *dws) dws->rx += dws->n_bytes; } - wait_till_not_busy(dws); return dws->rx == dws->rx_end; } @@ -368,13 +395,11 @@ 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) { - while (dw_writer(dws)) + do { + dw_writer(dws); dw_reader(dws); - /* - * There is a possibility that the last word of a transaction - * will be lost if data is not ready. Re-read to solve this issue. - */ - dw_reader(dws); + cpu_relax(); + } while (dws->rx_end > dws->rx); dw_spi_xfer_done(dws); } -- cgit v1.2.2 From 3b8a4dd3ebfcc647260ad5c39ef4f73eb3a6b155 Mon Sep 17 00:00:00 2001 From: Alek Du Date: Wed, 30 Mar 2011 23:09:55 +0800 Subject: spi/dw_spi: improve the interrupt mode with the batch ops leverage the performance gain by change in low level read/write batch operations Signed-off-by: Alek Du Signed-off-by: Feng Tang Signed-off-by: Grant Likely --- drivers/spi/dw_spi.c | 63 +++++++++++++--------------------------------------- 1 file changed, 16 insertions(+), 47 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 7a2a72268f0a..855ac4ae0f22 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -190,21 +190,7 @@ static inline u32 rx_max(struct dw_spi *dws) return min(rx_left, (u32)dw_readw(dws, rxflr)); } - -static void wait_till_not_busy(struct dw_spi *dws) -{ - unsigned long end = jiffies + 1 + usecs_to_jiffies(5000); - - while (time_before(jiffies, end)) { - if (!(dw_readw(dws, sr) & SR_BUSY)) - return; - cpu_relax(); - } - dev_err(&dws->master->dev, - "DW SPI: Status keeps busy for 5000us after a read/write!\n"); -} - -static int dw_writer(struct dw_spi *dws) +static void dw_writer(struct dw_spi *dws) { u32 max = tx_max(dws); u16 txw = 0; @@ -220,11 +206,9 @@ static int dw_writer(struct dw_spi *dws) dw_writew(dws, dr, txw); dws->tx += dws->n_bytes; } - - return 1; } -static int dw_reader(struct dw_spi *dws) +static void dw_reader(struct dw_spi *dws) { u32 max = rx_max(dws); u16 rxw; @@ -240,8 +224,6 @@ static int dw_reader(struct dw_spi *dws) } dws->rx += dws->n_bytes; } - - return dws->rx == dws->rx_end; } static void *next_transfer(struct dw_spi *dws) @@ -340,35 +322,28 @@ EXPORT_SYMBOL_GPL(dw_spi_xfer_done); static irqreturn_t interrupt_transfer(struct dw_spi *dws) { - u16 irq_status, irq_mask = 0x3f; - u32 int_level = dws->fifo_len / 2; - u32 left; + u16 irq_status = dw_readw(dws, isr); - irq_status = dw_readw(dws, isr) & irq_mask; /* Error handling */ if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) { dw_readw(dws, txoicr); dw_readw(dws, rxoicr); dw_readw(dws, rxuicr); - int_error_stop(dws, "interrupt_transfer: fifo overrun"); + int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun"); return IRQ_HANDLED; } + dw_reader(dws); + if (dws->rx_end == dws->rx) { + spi_mask_intr(dws, SPI_INT_TXEI); + dw_spi_xfer_done(dws); + return IRQ_HANDLED; + } 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--) - dw_writer(dws); - dw_reader(dws); - - /* 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 - dw_spi_xfer_done(dws); + dw_writer(dws); + /* Enable TX irq always, it will be disabled when RX finished */ + spi_umask_intr(dws, SPI_INT_TXEI); } return IRQ_HANDLED; @@ -377,15 +352,13 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) static irqreturn_t dw_spi_irq(int irq, void *dev_id) { struct dw_spi *dws = dev_id; - u16 irq_status, irq_mask = 0x3f; + u16 irq_status = dw_readw(dws, isr) & 0x3f; - irq_status = dw_readw(dws, isr) & irq_mask; if (!irq_status) return IRQ_NONE; if (!dws->cur_msg) { spi_mask_intr(dws, SPI_INT_TXEI); - /* Never fail */ return IRQ_HANDLED; } @@ -492,12 +465,8 @@ static void pump_transfers(unsigned long data) switch (bits) { case 8: - dws->n_bytes = 1; - dws->dma_width = 1; - break; case 16: - dws->n_bytes = 2; - dws->dma_width = 2; + dws->n_bytes = dws->dma_width = bits >> 3; break; default: printk(KERN_ERR "MRST SPI0: unsupported bits:" @@ -541,7 +510,7 @@ static void pump_transfers(unsigned long data) txint_level = dws->fifo_len / 2; txint_level = (templen > txint_level) ? txint_level : templen; - imask |= SPI_INT_TXEI; + imask |= SPI_INT_TXEI | SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI; dws->transfer_handler = interrupt_transfer; } -- cgit v1.2.2 From 25985edcedea6396277003854657b5f3cb31a628 Mon Sep 17 00:00:00 2001 From: Lucas De Marchi Date: Wed, 30 Mar 2011 22:57:33 -0300 Subject: Fix common misspellings Fixes generated by 'codespell' and manually reviewed. Signed-off-by: Lucas De Marchi --- drivers/spi/amba-pl022.c | 16 ++++++++-------- drivers/spi/au1550_spi.c | 2 +- drivers/spi/dw_spi.c | 2 +- drivers/spi/dw_spi.h | 2 +- drivers/spi/ep93xx_spi.c | 2 +- drivers/spi/pxa2xx_spi.c | 4 ++-- drivers/spi/spi.c | 2 +- drivers/spi/spi_bfin5xx.c | 2 +- drivers/spi/spi_fsl_espi.c | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 5a4e0afb9ad6..5825370bad25 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -661,7 +661,7 @@ static void readwriter(struct pl022 *pl022) { /* - * The FIFO depth is different inbetween primecell variants. + * The FIFO depth is different between primecell variants. * I believe filling in too much in the FIFO might cause * errons in 8bit wide transfers on ARM variants (just 8 words * FIFO, means only 8x8 = 64 bits in FIFO) at least. @@ -722,7 +722,7 @@ static void readwriter(struct pl022 *pl022) * This inner reader takes care of things appearing in the RX * FIFO as we're transmitting. This will happen a lot since the * clock starts running when you put things into the TX FIFO, - * and then things are continously clocked into the RX FIFO. + * and then things are continuously clocked into the RX FIFO. */ while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RNE) && (pl022->rx < pl022->rx_end)) { @@ -842,7 +842,7 @@ static void dma_callback(void *data) unmap_free_dma_scatter(pl022); - /* Update total bytes transfered */ + /* Update total bytes transferred */ msg->actual_length += pl022->cur_transfer->len; if (pl022->cur_transfer->cs_change) pl022->cur_chip-> @@ -1224,7 +1224,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id) "number of bytes on a 16bit bus?)\n", (u32) (pl022->rx - pl022->rx_end)); } - /* Update total bytes transfered */ + /* Update total bytes transferred */ msg->actual_length += pl022->cur_transfer->len; if (pl022->cur_transfer->cs_change) pl022->cur_chip-> @@ -1415,11 +1415,11 @@ static void do_polling_transfer(struct pl022 *pl022) SSP_CR1(pl022->virtbase)); dev_dbg(&pl022->adev->dev, "polling transfer ongoing ...\n"); - /* FIXME: insert a timeout so we don't hang here indefinately */ + /* FIXME: insert a timeout so we don't hang here indefinitely */ while (pl022->tx < pl022->tx_end || pl022->rx < pl022->rx_end) readwriter(pl022); - /* Update total byte transfered */ + /* Update total byte transferred */ message->actual_length += pl022->cur_transfer->len; if (pl022->cur_transfer->cs_change) pl022->cur_chip->cs_control(SSP_CHIP_DESELECT); @@ -2129,7 +2129,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) "probe - problem registering spi master\n"); goto err_spi_register; } - dev_dbg(dev, "probe succeded\n"); + dev_dbg(dev, "probe succeeded\n"); /* * Disable the silicon block pclk and any voltage domain and just * power it up and clock it when it's needed @@ -2184,7 +2184,7 @@ pl022_remove(struct amba_device *adev) spi_unregister_master(pl022->master); spi_master_put(pl022->master); amba_set_drvdata(adev, NULL); - dev_dbg(&adev->dev, "remove succeded\n"); + dev_dbg(&adev->dev, "remove succeeded\n"); return 0; } diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c index 3c9ade69643f..b50563d320e1 100644 --- a/drivers/spi/au1550_spi.c +++ b/drivers/spi/au1550_spi.c @@ -480,7 +480,7 @@ static irqreturn_t au1550_spi_dma_irq_callback(struct au1550_spi *hw) au1xxx_dbdma_stop(hw->dma_rx_ch); au1xxx_dbdma_stop(hw->dma_tx_ch); - /* get number of transfered bytes */ + /* get number of transferred bytes */ hw->rx_count = hw->len - au1xxx_get_dma_residue(hw->dma_rx_ch); hw->tx_count = hw->len - au1xxx_get_dma_residue(hw->dma_tx_ch); diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 9a6196461b27..b1a4b9f503ae 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -345,7 +345,7 @@ static void int_error_stop(struct dw_spi *dws, const char *msg) void dw_spi_xfer_done(struct dw_spi *dws) { - /* Update total byte transfered return count actual bytes read */ + /* Update total byte transferred return count actual bytes read */ dws->cur_msg->actual_length += dws->len; /* Move to next transfer */ diff --git a/drivers/spi/dw_spi.h b/drivers/spi/dw_spi.h index fb0bce564844..b23e452adaf7 100644 --- a/drivers/spi/dw_spi.h +++ b/drivers/spi/dw_spi.h @@ -46,7 +46,7 @@ #define SPI_INT_RXFI (1 << 4) #define SPI_INT_MSTI (1 << 5) -/* TX RX interrupt level threshhold, max can be 256 */ +/* TX RX interrupt level threshold, max can be 256 */ #define SPI_INT_THRESHOLD 32 enum dw_ssi_type { diff --git a/drivers/spi/ep93xx_spi.c b/drivers/spi/ep93xx_spi.c index 0ba35df9a6df..d3570071e98f 100644 --- a/drivers/spi/ep93xx_spi.c +++ b/drivers/spi/ep93xx_spi.c @@ -512,7 +512,7 @@ static int ep93xx_spi_read_write(struct ep93xx_spi *espi) * * This function processes one SPI transfer given in @t. Function waits until * transfer is complete (may sleep) and updates @msg->status based on whether - * transfer was succesfully processed or not. + * transfer was successfully processed or not. */ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi, struct spi_message *msg, diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index a429b01d0285..9c74aad6be93 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -700,7 +700,7 @@ static void int_transfer_complete(struct driver_data *drv_data) if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); - /* Update total byte transfered return count actual bytes read */ + /* Update total byte transferred return count actual bytes read */ drv_data->cur_msg->actual_length += drv_data->len - (drv_data->rx_end - drv_data->rx); @@ -759,7 +759,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) /* * PXA25x_SSP has no timeout, set up rx threshould for the - * remaing RX bytes. + * remaining RX bytes. */ if (pxa25x_ssp_comp(drv_data)) { diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 34bb17f03019..82b9a428c323 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -957,7 +957,7 @@ EXPORT_SYMBOL_GPL(spi_sync); * drivers may DMA directly into and out of the message buffers. * * This call should be used by drivers that require exclusive access to the - * SPI bus. It has to be preceeded by a spi_bus_lock call. The SPI bus must + * SPI bus. It has to be preceded by a spi_bus_lock call. The SPI bus must * be released by a spi_bus_unlock call when the exclusive access is over. * * It returns zero on success, else a negative error code. diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index a28462486df8..bdb7289a1d22 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -905,7 +905,7 @@ static void bfin_spi_pump_transfers(unsigned long data) "IO write error!\n"); message->state = ERROR_STATE; } else { - /* Update total byte transfered */ + /* Update total byte transferred */ message->actual_length += drv_data->len_in_bytes; /* Move to next transfer of this msg */ message->state = bfin_spi_next_transfer(drv_data); diff --git a/drivers/spi/spi_fsl_espi.c b/drivers/spi/spi_fsl_espi.c index 900e921ab80e..496f895a0024 100644 --- a/drivers/spi/spi_fsl_espi.c +++ b/drivers/spi/spi_fsl_espi.c @@ -474,7 +474,7 @@ static int fsl_espi_setup(struct spi_device *spi) mpc8xxx_spi = spi_master_get_devdata(spi->master); reg_base = mpc8xxx_spi->reg_base; - hw_mode = cs->hw_mode; /* Save orginal settings */ + hw_mode = cs->hw_mode; /* Save original settings */ cs->hw_mode = mpc8xxx_spi_read_reg( ®_base->csmode[spi->chip_select]); /* mask out bits we are going to set */ -- cgit v1.2.2 From 850a28ecd8044ef36b2c7699d2e3736a410b4d0a Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Wed, 6 Apr 2011 17:49:15 +0300 Subject: spi: Fix race condition in stop_queue() There's a race condition in stop_queue() in some drivers - if drv_data->queue is empty, but drv_data->busy is still set (or opposite situation) stop_queue will return -EBUSY. So fix loop condition to check that both drv_data->queue is empty and drv_data->busy is not set. This patch affects following drivers: pxa2xx_spi spi_bfin5xx amba-pl022 dw_spi Signed-off-by: Vasily Khoruzhick Acked-by: Eric Miao Acked-by: Mike Frysinger Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 2 +- drivers/spi/dw_spi.c | 2 +- drivers/spi/pxa2xx_spi.c | 2 +- drivers/spi/spi_bfin5xx.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 5a4e0afb9ad6..4b8357b728cb 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1555,7 +1555,7 @@ static int stop_queue(struct pl022 *pl022) * A wait_queue on the pl022->busy could be used, but then the common * execution path (pump_messages) would be required to call wake_up or * friends on every SPI message. Do this instead */ - while (!list_empty(&pl022->queue) && pl022->busy && limit--) { + while ((!list_empty(&pl022->queue) || pl022->busy) && limit--) { spin_unlock_irqrestore(&pl022->queue_lock, flags); msleep(10); spin_lock_irqsave(&pl022->queue_lock, flags); diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 9a6196461b27..d040debc07c2 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -821,7 +821,7 @@ static int stop_queue(struct dw_spi *dws) spin_lock_irqsave(&dws->lock, flags); dws->run = QUEUE_STOPPED; - while (!list_empty(&dws->queue) && dws->busy && limit--) { + while ((!list_empty(&dws->queue) || dws->busy) && limit--) { spin_unlock_irqrestore(&dws->lock, flags); msleep(10); spin_lock_irqsave(&dws->lock, flags); diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index a429b01d0285..3aa782067b68 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1493,7 +1493,7 @@ static int stop_queue(struct driver_data *drv_data) * execution path (pump_messages) would be required to call wake_up or * friends on every SPI message. Do this instead */ drv_data->run = QUEUE_STOPPED; - while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) { + while ((!list_empty(&drv_data->queue) || drv_data->busy) && limit--) { spin_unlock_irqrestore(&drv_data->lock, flags); msleep(10); spin_lock_irqsave(&drv_data->lock, flags); diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index a28462486df8..f3a1c52c0fb0 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -1284,7 +1284,7 @@ static inline int bfin_spi_stop_queue(struct bfin_spi_master_data *drv_data) * friends on every SPI message. Do this instead */ drv_data->running = false; - while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) { + while ((!list_empty(&drv_data->queue) || drv_data->busy) && limit--) { spin_unlock_irqrestore(&drv_data->lock, flags); msleep(10); spin_lock_irqsave(&drv_data->lock, flags); -- cgit v1.2.2 From e05503ef1186ad33dfe56794407891eb1dd93ef6 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 18 May 2011 16:49:24 +0200 Subject: Haavard Skinnemoen has left Atmel Haavard's e-mail address at Atmel is no longer valid. Signed-off-by: Jean Delvare Acked-by: Havard Skinnemoen Signed-off-by: Jiri Kosina --- drivers/spi/atmel_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 1a478bf88c9d..08711e9202ab 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -935,6 +935,6 @@ static void __exit atmel_spi_exit(void) module_exit(atmel_spi_exit); MODULE_DESCRIPTION("Atmel AT32/AT91 SPI Controller driver"); -MODULE_AUTHOR("Haavard Skinnemoen "); +MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:atmel_spi"); -- cgit v1.2.2 From a18c266f8e43004c85c56b4077f6158fcadb7707 Mon Sep 17 00:00:00 2001 From: Magnus Templing Date: Thu, 19 May 2011 18:05:34 +0200 Subject: spi/pl022: timeout on polled transfer v2 This adds the missing handling of polling timeouts and deletes our last todo. Signed-off-by: Magnus Templing Reviewed-by: Srinidhi Kasagar [Fixups from review by Wolfram Sang] Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 08de58e7f59f..18667de436f1 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -24,11 +24,6 @@ * GNU General Public License for more details. */ -/* - * TODO: - * - add timeout on polled transfers - */ - #include #include #include @@ -287,6 +282,8 @@ #define CLEAR_ALL_INTERRUPTS 0x3 +#define SPI_POLLING_TIMEOUT 1000 + /* * The type of reading going on on this chip @@ -1378,6 +1375,7 @@ static void do_polling_transfer(struct pl022 *pl022) struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; struct chip_data *chip; + unsigned long time, timeout; chip = pl022->cur_chip; message = pl022->cur_msg; @@ -1415,9 +1413,18 @@ static void do_polling_transfer(struct pl022 *pl022) SSP_CR1(pl022->virtbase)); dev_dbg(&pl022->adev->dev, "polling transfer ongoing ...\n"); - /* FIXME: insert a timeout so we don't hang here indefinitely */ - while (pl022->tx < pl022->tx_end || pl022->rx < pl022->rx_end) + + timeout = jiffies + msecs_to_jiffies(SPI_POLLING_TIMEOUT); + while (pl022->tx < pl022->tx_end || pl022->rx < pl022->rx_end) { + time = jiffies; readwriter(pl022); + if (time_after(time, timeout)) { + dev_warn(&pl022->adev->dev, + "%s: timeout!\n", __func__); + message->state = STATE_ERROR; + goto out; + } + } /* Update total byte transferred */ message->actual_length += pl022->cur_transfer->len; @@ -1426,7 +1433,7 @@ static void do_polling_transfer(struct pl022 *pl022) /* Move to next transfer */ message->state = next_transfer(pl022); } - +out: /* Handle end of message */ if (message->state == STATE_DONE) message->status = 0; -- cgit v1.2.2 From 7f9a4b9797405061a07fca26ff1b4f305c564e5d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 19 May 2011 14:13:19 +0200 Subject: spi/pl022: mark driver non-experimental This driver has no TODO, and is now used on several platforms: ARM, U300, Ux500, SPEAr and more. So drop the EXPERIMENTAL requirement. Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index fc14b8dea0d7..fbd96b29530d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -271,8 +271,8 @@ config SPI_ORION This enables using the SPI master controller on the Orion chips. config SPI_PL022 - tristate "ARM AMBA PL022 SSP controller (EXPERIMENTAL)" - depends on ARM_AMBA && EXPERIMENTAL + tristate "ARM AMBA PL022 SSP controller" + depends on ARM_AMBA default y if MACH_U300 default y if ARCH_REALVIEW default y if INTEGRATOR_IMPD1 -- cgit v1.2.2 From 521999bd4a8c47a86136b9d8223a9480f5906db8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 19 May 2011 20:01:25 +0200 Subject: spi/pl022: use cpu_relax in the busy loop This relaxes the cpu in the polling busy-wait loop. Reported-by: Vitaly Wool Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 18667de436f1..8a2b88aee373 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1424,6 +1424,7 @@ static void do_polling_transfer(struct pl022 *pl022) message->state = STATE_ERROR; goto out; } + cpu_relax(); } /* Update total byte transferred */ -- cgit v1.2.2 From 0c4a1590189b426814748d2e03b95541852b3af6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 May 2011 00:09:30 +0200 Subject: spi: Use void pointers for data in simple SPI I/O operations Currently the simple SPI I/O operations all take pointers to u8 * buffers to operate on. This creates needless type compatibility issues and the underlying spi_transfer structure uses void pointers anyway so convert the API over to take void pointers too. Signed-off-by: Mark Brown Signed-off-by: Grant Likely --- drivers/spi/spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 82b9a428c323..2e13a14bba3f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1047,8 +1047,8 @@ static u8 *buf; * spi_{async,sync}() calls with dma-safe buffers. */ int spi_write_then_read(struct spi_device *spi, - const u8 *txbuf, unsigned n_tx, - u8 *rxbuf, unsigned n_rx) + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx) { static DEFINE_MUTEX(lock); -- cgit v1.2.2 From 680c1305e259a488f489bc887854523b6c6e0705 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 11 May 2011 21:27:00 +0800 Subject: spi/spi_sh: use spi_unregister_master instead of spi_master_put in remove path spi_master_put() should only be used in error handling. Once spi_register_master() returns success, we should call spi_unregister_master() instead. Signed-off-by: Axel Lin Signed-off-by: Grant Likely --- drivers/spi/spi_sh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_sh.c b/drivers/spi/spi_sh.c index 869a07d375d6..9eedd71ad898 100644 --- a/drivers/spi/spi_sh.c +++ b/drivers/spi/spi_sh.c @@ -427,10 +427,10 @@ static int __devexit spi_sh_remove(struct platform_device *pdev) { struct spi_sh_data *ss = dev_get_drvdata(&pdev->dev); + spi_unregister_master(ss->master); destroy_workqueue(ss->workqueue); free_irq(ss->irq, ss); iounmap(ss->addr); - spi_master_put(ss->master); return 0; } -- cgit v1.2.2 From 8901e1b98e1dad75c4567d03dd00a59bd6c450e8 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 11 May 2011 21:28:16 +0800 Subject: spi/spi_tegra: use spi_unregister_master() instead of spi_master_put() spi_master_put() should only be used in error handling. Once spi_register_master() returns success, we should call spi_unregister_master() instead. Signed-off-by: Axel Lin Signed-off-by: Grant Likely --- drivers/spi/spi_tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_tegra.c b/drivers/spi/spi_tegra.c index 891e5909038c..6c3aa6ecaade 100644 --- a/drivers/spi/spi_tegra.c +++ b/drivers/spi/spi_tegra.c @@ -578,6 +578,7 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) master = dev_get_drvdata(&pdev->dev); tspi = spi_master_get_devdata(master); + spi_unregister_master(master); tegra_dma_free_channel(tspi->rx_dma); dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN, @@ -586,7 +587,6 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) clk_put(tspi->clk); iounmap(tspi->base); - spi_master_put(master); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(r->start, (r->end - r->start) + 1); -- cgit v1.2.2 From 708a7e438806c02add92a585b0a6b4b2ae50159b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 15 May 2011 07:33:28 +0800 Subject: spi/spi_nuc900: Use spi_bitbang_stop instead of spi_unregister_master in nuc900_spi_remove Calling spi_bitbang_stop() will also destroy bitbang->workqueue, which is created by calling spi_bitbang_start() in nuc900_spi_probe(). Signed-off-by: Axel Lin Signed-off-by: Grant Likely --- drivers/spi/spi_nuc900.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_nuc900.c b/drivers/spi/spi_nuc900.c index d5be18b3078c..3cd15f690f16 100644 --- a/drivers/spi/spi_nuc900.c +++ b/drivers/spi/spi_nuc900.c @@ -463,7 +463,7 @@ static int __devexit nuc900_spi_remove(struct platform_device *dev) platform_set_drvdata(dev, NULL); - spi_unregister_master(hw->master); + spi_bitbang_stop(&hw->bitbang); clk_disable(hw->clk); clk_put(hw->clk); -- cgit v1.2.2 From c6e7b8cb11632a3b3968c6f64e179c7619eb70c0 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 15 May 2011 07:35:16 +0800 Subject: spi/spi_s3c24xx: Use spi_bitbang_stop instead of spi_unregister_master in s3c24xx_spi_remove Calling spi_bitbang_stop() will also destroy bitbang->workqueue, which is created by calling spi_bitbang_start() in s3c24xx_spi_probe(). Signed-off-by: Axel Lin Signed-off-by: Grant Likely --- drivers/spi/spi_s3c24xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 151a95e40653..1a5fcabfd565 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -668,7 +668,7 @@ static int __exit s3c24xx_spi_remove(struct platform_device *dev) platform_set_drvdata(dev, NULL); - spi_unregister_master(hw->master); + spi_bitbang_stop(&hw->bitbang); clk_disable(hw->clk); clk_put(hw->clk); -- cgit v1.2.2 From 43c640157d4366a3ea9ba01f903ea892f46376ee Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 16 May 2011 09:40:10 +0530 Subject: spi/amba-pl022: work in polling or interrupt mode if pl022_dma_probe fails If pl022_dma_probe fails, we can try to transfer data in polling or interrupt mode. Also, set platform_info->enable_dma to 0, so that no other code tries to use dma. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 8a2b88aee373..6a9e58dd36c7 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1060,7 +1060,7 @@ static int __init pl022_dma_probe(struct pl022 *pl022) pl022->master_info->dma_filter, pl022->master_info->dma_rx_param); if (!pl022->dma_rx_channel) { - dev_err(&pl022->adev->dev, "no RX DMA channel!\n"); + dev_dbg(&pl022->adev->dev, "no RX DMA channel!\n"); goto err_no_rxchan; } @@ -1068,13 +1068,13 @@ static int __init pl022_dma_probe(struct pl022 *pl022) pl022->master_info->dma_filter, pl022->master_info->dma_tx_param); if (!pl022->dma_tx_channel) { - dev_err(&pl022->adev->dev, "no TX DMA channel!\n"); + dev_dbg(&pl022->adev->dev, "no TX DMA channel!\n"); goto err_no_txchan; } pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!pl022->dummypage) { - dev_err(&pl022->adev->dev, "no DMA dummypage!\n"); + dev_dbg(&pl022->adev->dev, "no DMA dummypage!\n"); goto err_no_dummypage; } @@ -1090,6 +1090,8 @@ err_no_txchan: dma_release_channel(pl022->dma_rx_channel); pl022->dma_rx_channel = NULL; err_no_rxchan: + dev_err(&pl022->adev->dev, + "Failed to work in dma mode, work without dma!\n"); return -ENODEV; } @@ -2115,7 +2117,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) if (platform_info->enable_dma) { status = pl022_dma_probe(pl022); if (status != 0) - goto err_no_dma; + platform_info->enable_dma = 0; } /* Initialize and start queue */ @@ -2151,7 +2153,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) err_init_queue: destroy_queue(pl022); pl022_dma_remove(pl022); - err_no_dma: free_irq(adev->irq[0], pl022); err_no_irq: clk_put(pl022->clk); -- cgit v1.2.2 From 0b4bf78214efcdadbbe5b09c5dff35dcc9bcb3df Mon Sep 17 00:00:00 2001 From: Steven King Date: Sun, 24 Apr 2011 10:48:07 -0700 Subject: coldfire_qspi compile fix The m68k/m68knommu merge broke the qspi build. Signed-off-by: Steven King Signed-off-by: Greg Ungerer --- drivers/spi/coldfire_qspi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/coldfire_qspi.c b/drivers/spi/coldfire_qspi.c index 8856bcca9d29..ae2cd1c1fda8 100644 --- a/drivers/spi/coldfire_qspi.c +++ b/drivers/spi/coldfire_qspi.c @@ -33,6 +33,7 @@ #include #include +#include #include #define DRIVER_NAME "mcfqspi" -- cgit v1.2.2 From 3271d382c3ffe61ef3d059ef47e635dbe031030e Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 8 Apr 2011 01:23:57 +0200 Subject: mfd: Use mfd cell platform_data for timberdale cells platform bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the addition of a device platform mfd_cell pointer, MFD drivers can go back to passing platform data back to their sub drivers. This allows for an mfd_cell->mfd_data removal and thus keep the sub drivers MFD agnostic. This is mostly needed for non MFD aware sub drivers. Acked-by: Richard Röjfors Signed-off-by: Samuel Ortiz --- drivers/spi/xilinx_spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index c69c6f2c2c5c..4d2c75df886c 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -471,7 +470,7 @@ static int __devinit xilinx_spi_probe(struct platform_device *dev) struct spi_master *master; u8 i; - pdata = mfd_get_data(dev); + pdata = dev->dev.platform_data; if (pdata) { num_cs = pdata->num_chipselect; little_endian = pdata->little_endian; -- cgit v1.2.2 From 80b4037033c2dae31e73810d506ce93b3783be05 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 11 May 2011 20:39:05 +0800 Subject: spi/tle620x: add missing device_remove_file() This patch includes below fixes: 1. Add missing device_remove_file for dev_attr_status_show in tle62x0_remove. 2. Fix tle62x0_probe error handling: Currently, if the error happens when ptr > 0, gpio_attrs[0] is not properly remove. Signed-off-by: Axel Lin Acked-by: Ben Dooks Signed-off-by: Grant Likely --- drivers/spi/tle62x0.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/tle62x0.c b/drivers/spi/tle62x0.c index a3938958147c..32a40876532f 100644 --- a/drivers/spi/tle62x0.c +++ b/drivers/spi/tle62x0.c @@ -283,7 +283,7 @@ static int __devinit tle62x0_probe(struct spi_device *spi) return 0; err_gpios: - for (; ptr > 0; ptr--) + while (--ptr >= 0) device_remove_file(&spi->dev, gpio_attrs[ptr]); device_remove_file(&spi->dev, &dev_attr_status_show); @@ -301,6 +301,7 @@ static int __devexit tle62x0_remove(struct spi_device *spi) for (ptr = 0; ptr < st->nr_gpio; ptr++) device_remove_file(&spi->dev, gpio_attrs[ptr]); + device_remove_file(&spi->dev, &dev_attr_status_show); kfree(st); return 0; } -- cgit v1.2.2 From 9c3e737561b2473ea1fc5b83a0c1d51e1ff3d294 Mon Sep 17 00:00:00 2001 From: Cliff Cai Date: Mon, 28 Mar 2011 04:57:11 -0400 Subject: spi/spi_bfin_sport: new driver for a SPI bus via the Blackfin SPORT peripheral The Blackfin SPORT peripheral is a pretty flexible device. With enough coaching, we can make it generate SPI compatible waveforms. This is desirable as the SPORT can run at much higher clock frequencies than the dedicated on-chip SPI peripheral, and it can do full duplex DMA. It also opens up the possibility of multiple SPI buses in case someone wants to dedicate a whole bus to a specific part that does not play well with others. Signed-off-by: Cliff Cai Signed-off-by: Bryan Wu Signed-off-by: Michael Hennerich Signed-off-by: Mike Frysinger Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi_bfin_sport.c | 952 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 962 insertions(+) create mode 100644 drivers/spi/spi_bfin_sport.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index fbd96b29530d..de35c3ad8a69 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -80,6 +80,15 @@ config SPI_BFIN help This is the SPI controller master driver for Blackfin 5xx processor. +config SPI_BFIN_SPORT + tristate "SPI bus via Blackfin SPORT" + depends on BLACKFIN + help + Enable support for a SPI bus via the Blackfin SPORT peripheral. + + This driver can also be built as a module. If so, the module + will be called spi_bfin_sport. + config SPI_AU1550 tristate "Au1550/Au12x0 SPI Controller" depends on (SOC_AU1550 || SOC_AU1200) && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index fd2fc5f6505f..0f8c69b6b19e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi_altera.o obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o obj-$(CONFIG_SPI_ATH79) += ath79_spi.o obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o +obj-$(CONFIG_SPI_BFIN_SPORT) += spi_bfin_sport.o obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_AU1550) += au1550_spi.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o diff --git a/drivers/spi/spi_bfin_sport.c b/drivers/spi/spi_bfin_sport.c new file mode 100644 index 000000000000..e557ff617b11 --- /dev/null +++ b/drivers/spi/spi_bfin_sport.c @@ -0,0 +1,952 @@ +/* + * SPI bus via the Blackfin SPORT peripheral + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright 2009-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DRV_NAME "bfin-sport-spi" +#define DRV_DESC "SPI bus via the Blackfin SPORT" + +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION(DRV_DESC); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bfin-sport-spi"); + +enum bfin_sport_spi_state { + START_STATE, + RUNNING_STATE, + DONE_STATE, + ERROR_STATE, +}; + +struct bfin_sport_spi_master_data; + +struct bfin_sport_transfer_ops { + void (*write) (struct bfin_sport_spi_master_data *); + void (*read) (struct bfin_sport_spi_master_data *); + void (*duplex) (struct bfin_sport_spi_master_data *); +}; + +struct bfin_sport_spi_master_data { + /* Driver model hookup */ + struct device *dev; + + /* SPI framework hookup */ + struct spi_master *master; + + /* Regs base of SPI controller */ + struct sport_register __iomem *regs; + int err_irq; + + /* Pin request list */ + u16 *pin_req; + + /* Driver message queue */ + struct workqueue_struct *workqueue; + struct work_struct pump_messages; + spinlock_t lock; + struct list_head queue; + int busy; + bool run; + + /* Message Transfer pump */ + struct tasklet_struct pump_transfers; + + /* Current message transfer state info */ + enum bfin_sport_spi_state state; + struct spi_message *cur_msg; + struct spi_transfer *cur_transfer; + struct bfin_sport_spi_slave_data *cur_chip; + union { + void *tx; + u8 *tx8; + u16 *tx16; + }; + void *tx_end; + union { + void *rx; + u8 *rx8; + u16 *rx16; + }; + void *rx_end; + + int cs_change; + struct bfin_sport_transfer_ops *ops; +}; + +struct bfin_sport_spi_slave_data { + u16 ctl_reg; + u16 baud; + u16 cs_chg_udelay; /* Some devices require > 255usec delay */ + u32 cs_gpio; + u16 idle_tx_val; + struct bfin_sport_transfer_ops *ops; +}; + +static void +bfin_sport_spi_enable(struct bfin_sport_spi_master_data *drv_data) +{ + bfin_write_or(&drv_data->regs->tcr1, TSPEN); + bfin_write_or(&drv_data->regs->rcr1, TSPEN); + SSYNC(); +} + +static void +bfin_sport_spi_disable(struct bfin_sport_spi_master_data *drv_data) +{ + bfin_write_and(&drv_data->regs->tcr1, ~TSPEN); + bfin_write_and(&drv_data->regs->rcr1, ~TSPEN); + SSYNC(); +} + +/* Caculate the SPI_BAUD register value based on input HZ */ +static u16 +bfin_sport_hz_to_spi_baud(u32 speed_hz) +{ + u_long clk, sclk = get_sclk(); + int div = (sclk / (2 * speed_hz)) - 1; + + if (div < 0) + div = 0; + + clk = sclk / (2 * (div + 1)); + + if (clk > speed_hz) + div++; + + return div; +} + +/* Chip select operation functions for cs_change flag */ +static void +bfin_sport_spi_cs_active(struct bfin_sport_spi_slave_data *chip) +{ + gpio_direction_output(chip->cs_gpio, 0); +} + +static void +bfin_sport_spi_cs_deactive(struct bfin_sport_spi_slave_data *chip) +{ + gpio_direction_output(chip->cs_gpio, 1); + /* Move delay here for consistency */ + if (chip->cs_chg_udelay) + udelay(chip->cs_chg_udelay); +} + +static void +bfin_sport_spi_stat_poll_complete(struct bfin_sport_spi_master_data *drv_data) +{ + unsigned long timeout = jiffies + HZ; + while (!(bfin_read(&drv_data->regs->stat) & RXNE)) { + if (!time_before(jiffies, timeout)) + break; + } +} + +static void +bfin_sport_spi_u8_writer(struct bfin_sport_spi_master_data *drv_data) +{ + u16 dummy; + + while (drv_data->tx < drv_data->tx_end) { + bfin_write(&drv_data->regs->tx16, *drv_data->tx8++); + bfin_sport_spi_stat_poll_complete(drv_data); + dummy = bfin_read(&drv_data->regs->rx16); + } +} + +static void +bfin_sport_spi_u8_reader(struct bfin_sport_spi_master_data *drv_data) +{ + u16 tx_val = drv_data->cur_chip->idle_tx_val; + + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tx16, tx_val); + bfin_sport_spi_stat_poll_complete(drv_data); + *drv_data->rx8++ = bfin_read(&drv_data->regs->rx16); + } +} + +static void +bfin_sport_spi_u8_duplex(struct bfin_sport_spi_master_data *drv_data) +{ + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tx16, *drv_data->tx8++); + bfin_sport_spi_stat_poll_complete(drv_data); + *drv_data->rx8++ = bfin_read(&drv_data->regs->rx16); + } +} + +static struct bfin_sport_transfer_ops bfin_sport_transfer_ops_u8 = { + .write = bfin_sport_spi_u8_writer, + .read = bfin_sport_spi_u8_reader, + .duplex = bfin_sport_spi_u8_duplex, +}; + +static void +bfin_sport_spi_u16_writer(struct bfin_sport_spi_master_data *drv_data) +{ + u16 dummy; + + while (drv_data->tx < drv_data->tx_end) { + bfin_write(&drv_data->regs->tx16, *drv_data->tx16++); + bfin_sport_spi_stat_poll_complete(drv_data); + dummy = bfin_read(&drv_data->regs->rx16); + } +} + +static void +bfin_sport_spi_u16_reader(struct bfin_sport_spi_master_data *drv_data) +{ + u16 tx_val = drv_data->cur_chip->idle_tx_val; + + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tx16, tx_val); + bfin_sport_spi_stat_poll_complete(drv_data); + *drv_data->rx16++ = bfin_read(&drv_data->regs->rx16); + } +} + +static void +bfin_sport_spi_u16_duplex(struct bfin_sport_spi_master_data *drv_data) +{ + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tx16, *drv_data->tx16++); + bfin_sport_spi_stat_poll_complete(drv_data); + *drv_data->rx16++ = bfin_read(&drv_data->regs->rx16); + } +} + +static struct bfin_sport_transfer_ops bfin_sport_transfer_ops_u16 = { + .write = bfin_sport_spi_u16_writer, + .read = bfin_sport_spi_u16_reader, + .duplex = bfin_sport_spi_u16_duplex, +}; + +/* stop controller and re-config current chip */ +static void +bfin_sport_spi_restore_state(struct bfin_sport_spi_master_data *drv_data) +{ + struct bfin_sport_spi_slave_data *chip = drv_data->cur_chip; + unsigned int bits = (drv_data->ops == &bfin_sport_transfer_ops_u8 ? 7 : 15); + + bfin_sport_spi_disable(drv_data); + dev_dbg(drv_data->dev, "restoring spi ctl state\n"); + + bfin_write(&drv_data->regs->tcr1, chip->ctl_reg); + bfin_write(&drv_data->regs->tcr2, bits); + bfin_write(&drv_data->regs->tclkdiv, chip->baud); + bfin_write(&drv_data->regs->tfsdiv, bits); + SSYNC(); + + bfin_write(&drv_data->regs->rcr1, chip->ctl_reg & ~(ITCLK | ITFS)); + bfin_write(&drv_data->regs->rcr2, bits); + SSYNC(); + + bfin_sport_spi_cs_active(chip); +} + +/* test if there is more transfer to be done */ +static enum bfin_sport_spi_state +bfin_sport_spi_next_transfer(struct bfin_sport_spi_master_data *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + struct spi_transfer *trans = drv_data->cur_transfer; + + /* Move to next transfer */ + if (trans->transfer_list.next != &msg->transfers) { + drv_data->cur_transfer = + list_entry(trans->transfer_list.next, + struct spi_transfer, transfer_list); + return RUNNING_STATE; + } + + return DONE_STATE; +} + +/* + * caller already set message->status; + * dma and pio irqs are blocked give finished message back + */ +static void +bfin_sport_spi_giveback(struct bfin_sport_spi_master_data *drv_data) +{ + struct bfin_sport_spi_slave_data *chip = drv_data->cur_chip; + unsigned long flags; + struct spi_message *msg; + + spin_lock_irqsave(&drv_data->lock, flags); + msg = drv_data->cur_msg; + drv_data->state = START_STATE; + 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); + + if (!drv_data->cs_change) + bfin_sport_spi_cs_deactive(chip); + + if (msg->complete) + msg->complete(msg->context); +} + +static irqreturn_t +sport_err_handler(int irq, void *dev_id) +{ + struct bfin_sport_spi_master_data *drv_data = dev_id; + u16 status; + + dev_dbg(drv_data->dev, "%s enter\n", __func__); + status = bfin_read(&drv_data->regs->stat) & (TOVF | TUVF | ROVF | RUVF); + + if (status) { + bfin_write(&drv_data->regs->stat, status); + SSYNC(); + + bfin_sport_spi_disable(drv_data); + dev_err(drv_data->dev, "status error:%s%s%s%s\n", + status & TOVF ? " TOVF" : "", + status & TUVF ? " TUVF" : "", + status & ROVF ? " ROVF" : "", + status & RUVF ? " RUVF" : ""); + } + + return IRQ_HANDLED; +} + +static void +bfin_sport_spi_pump_transfers(unsigned long data) +{ + struct bfin_sport_spi_master_data *drv_data = (void *)data; + struct spi_message *message = NULL; + struct spi_transfer *transfer = NULL; + struct spi_transfer *previous = NULL; + struct bfin_sport_spi_slave_data *chip = NULL; + unsigned int bits_per_word; + u32 tranf_success = 1; + u32 transfer_speed; + u8 full_duplex = 0; + + /* Get current state information */ + message = drv_data->cur_msg; + transfer = drv_data->cur_transfer; + chip = drv_data->cur_chip; + + if (transfer->speed_hz) + transfer_speed = bfin_sport_hz_to_spi_baud(transfer->speed_hz); + else + transfer_speed = chip->baud; + bfin_write(&drv_data->regs->tclkdiv, transfer_speed); + SSYNC(); + + /* + * if msg is error or done, report it back using complete() callback + */ + + /* Handle for abort */ + if (drv_data->state == ERROR_STATE) { + dev_dbg(drv_data->dev, "transfer: we've hit an error\n"); + message->status = -EIO; + bfin_sport_spi_giveback(drv_data); + return; + } + + /* Handle end of message */ + if (drv_data->state == DONE_STATE) { + dev_dbg(drv_data->dev, "transfer: all done!\n"); + message->status = 0; + bfin_sport_spi_giveback(drv_data); + return; + } + + /* Delay if requested at end of transfer */ + if (drv_data->state == RUNNING_STATE) { + dev_dbg(drv_data->dev, "transfer: still running ...\n"); + previous = list_entry(transfer->transfer_list.prev, + struct spi_transfer, transfer_list); + if (previous->delay_usecs) + udelay(previous->delay_usecs); + } + + if (transfer->len == 0) { + /* Move to next transfer of this msg */ + drv_data->state = bfin_sport_spi_next_transfer(drv_data); + /* Schedule next transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + } + + if (transfer->tx_buf != NULL) { + drv_data->tx = (void *)transfer->tx_buf; + drv_data->tx_end = drv_data->tx + transfer->len; + dev_dbg(drv_data->dev, "tx_buf is %p, tx_end is %p\n", + transfer->tx_buf, drv_data->tx_end); + } else + drv_data->tx = NULL; + + if (transfer->rx_buf != NULL) { + full_duplex = transfer->tx_buf != NULL; + drv_data->rx = transfer->rx_buf; + drv_data->rx_end = drv_data->rx + transfer->len; + dev_dbg(drv_data->dev, "rx_buf is %p, rx_end is %p\n", + transfer->rx_buf, drv_data->rx_end); + } else + drv_data->rx = NULL; + + drv_data->cs_change = transfer->cs_change; + + /* Bits per word setup */ + bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word; + if (bits_per_word == 8) + drv_data->ops = &bfin_sport_transfer_ops_u8; + else + drv_data->ops = &bfin_sport_transfer_ops_u16; + + drv_data->state = RUNNING_STATE; + + if (drv_data->cs_change) + bfin_sport_spi_cs_active(chip); + + dev_dbg(drv_data->dev, + "now pumping a transfer: width is %d, len is %d\n", + bits_per_word, transfer->len); + + /* PIO mode write then read */ + dev_dbg(drv_data->dev, "doing IO transfer\n"); + + bfin_sport_spi_enable(drv_data); + if (full_duplex) { + /* full duplex mode */ + BUG_ON((drv_data->tx_end - drv_data->tx) != + (drv_data->rx_end - drv_data->rx)); + drv_data->ops->duplex(drv_data); + + if (drv_data->tx != drv_data->tx_end) + tranf_success = 0; + } else if (drv_data->tx != NULL) { + /* write only half duplex */ + + drv_data->ops->write(drv_data); + + if (drv_data->tx != drv_data->tx_end) + tranf_success = 0; + } else if (drv_data->rx != NULL) { + /* read only half duplex */ + + drv_data->ops->read(drv_data); + if (drv_data->rx != drv_data->rx_end) + tranf_success = 0; + } + bfin_sport_spi_disable(drv_data); + + if (!tranf_success) { + dev_dbg(drv_data->dev, "IO write error!\n"); + drv_data->state = ERROR_STATE; + } else { + /* Update total byte transfered */ + message->actual_length += transfer->len; + /* Move to next transfer of this msg */ + drv_data->state = bfin_sport_spi_next_transfer(drv_data); + if (drv_data->cs_change) + bfin_sport_spi_cs_deactive(chip); + } + + /* Schedule next transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); +} + +/* pop a msg from queue and kick off real transfer */ +static void +bfin_sport_spi_pump_messages(struct work_struct *work) +{ + struct bfin_sport_spi_master_data *drv_data; + unsigned long flags; + struct spi_message *next_msg; + + drv_data = container_of(work, struct bfin_sport_spi_master_data, pump_messages); + + /* Lock queue and check for queue work */ + spin_lock_irqsave(&drv_data->lock, flags); + if (list_empty(&drv_data->queue) || !drv_data->run) { + /* pumper kicked off but no work to do */ + drv_data->busy = 0; + spin_unlock_irqrestore(&drv_data->lock, flags); + return; + } + + /* Make sure we are not already running a message */ + if (drv_data->cur_msg) { + spin_unlock_irqrestore(&drv_data->lock, flags); + return; + } + + /* Extract head of queue */ + next_msg = list_entry(drv_data->queue.next, + struct spi_message, queue); + + drv_data->cur_msg = next_msg; + + /* Setup the SSP using the per chip configuration */ + drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); + + list_del_init(&drv_data->cur_msg->queue); + + /* Initialize message state */ + drv_data->cur_msg->state = START_STATE; + drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, + struct spi_transfer, transfer_list); + bfin_sport_spi_restore_state(drv_data); + dev_dbg(drv_data->dev, "got a message to pump, " + "state is set to: baud %d, cs_gpio %i, ctl 0x%x\n", + drv_data->cur_chip->baud, drv_data->cur_chip->cs_gpio, + drv_data->cur_chip->ctl_reg); + + dev_dbg(drv_data->dev, + "the first transfer len is %d\n", + drv_data->cur_transfer->len); + + /* Mark as busy and launch transfers */ + tasklet_schedule(&drv_data->pump_transfers); + + drv_data->busy = 1; + spin_unlock_irqrestore(&drv_data->lock, flags); +} + +/* + * got a msg to transfer, queue it in drv_data->queue. + * And kick off message pumper + */ +static int +bfin_sport_spi_transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct bfin_sport_spi_master_data *drv_data = spi_master_get_devdata(spi->master); + unsigned long flags; + + spin_lock_irqsave(&drv_data->lock, flags); + + if (!drv_data->run) { + spin_unlock_irqrestore(&drv_data->lock, flags); + return -ESHUTDOWN; + } + + msg->actual_length = 0; + msg->status = -EINPROGRESS; + msg->state = START_STATE; + + dev_dbg(&spi->dev, "adding an msg in transfer()\n"); + list_add_tail(&msg->queue, &drv_data->queue); + + if (drv_data->run && !drv_data->busy) + queue_work(drv_data->workqueue, &drv_data->pump_messages); + + spin_unlock_irqrestore(&drv_data->lock, flags); + + return 0; +} + +/* Called every time common spi devices change state */ +static int +bfin_sport_spi_setup(struct spi_device *spi) +{ + struct bfin_sport_spi_slave_data *chip, *first = NULL; + int ret; + + /* Only alloc (or use chip_info) on first setup */ + chip = spi_get_ctldata(spi); + if (chip == NULL) { + struct bfin5xx_spi_chip *chip_info; + + chip = first = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + /* platform chip_info isn't required */ + chip_info = spi->controller_data; + if (chip_info) { + /* + * DITFS and TDTYPE are only thing we don't set, but + * they probably shouldn't be changed by people. + */ + if (chip_info->ctl_reg || chip_info->enable_dma) { + ret = -EINVAL; + dev_err(&spi->dev, "don't set ctl_reg/enable_dma fields"); + goto error; + } + chip->cs_chg_udelay = chip_info->cs_chg_udelay; + chip->idle_tx_val = chip_info->idle_tx_val; + spi->bits_per_word = chip_info->bits_per_word; + } + } + + if (spi->bits_per_word != 8 && spi->bits_per_word != 16) { + ret = -EINVAL; + goto error; + } + + /* translate common spi framework into our register + * following configure contents are same for tx and rx. + */ + + if (spi->mode & SPI_CPHA) + chip->ctl_reg &= ~TCKFE; + else + chip->ctl_reg |= TCKFE; + + if (spi->mode & SPI_LSB_FIRST) + chip->ctl_reg |= TLSBIT; + else + chip->ctl_reg &= ~TLSBIT; + + /* Sport in master mode */ + chip->ctl_reg |= ITCLK | ITFS | TFSR | LATFS | LTFS; + + chip->baud = bfin_sport_hz_to_spi_baud(spi->max_speed_hz); + + chip->cs_gpio = spi->chip_select; + ret = gpio_request(chip->cs_gpio, spi->modalias); + if (ret) + goto error; + + dev_dbg(&spi->dev, "setup spi chip %s, width is %d\n", + spi->modalias, spi->bits_per_word); + dev_dbg(&spi->dev, "ctl_reg is 0x%x, GPIO is %i\n", + chip->ctl_reg, spi->chip_select); + + spi_set_ctldata(spi, chip); + + bfin_sport_spi_cs_deactive(chip); + + return ret; + + error: + kfree(first); + return ret; +} + +/* + * callback for spi framework. + * clean driver specific data + */ +static void +bfin_sport_spi_cleanup(struct spi_device *spi) +{ + struct bfin_sport_spi_slave_data *chip = spi_get_ctldata(spi); + + if (!chip) + return; + + gpio_free(chip->cs_gpio); + + kfree(chip); +} + +static int +bfin_sport_spi_init_queue(struct bfin_sport_spi_master_data *drv_data) +{ + INIT_LIST_HEAD(&drv_data->queue); + spin_lock_init(&drv_data->lock); + + drv_data->run = false; + drv_data->busy = 0; + + /* init transfer tasklet */ + tasklet_init(&drv_data->pump_transfers, + bfin_sport_spi_pump_transfers, (unsigned long)drv_data); + + /* init messages workqueue */ + INIT_WORK(&drv_data->pump_messages, bfin_sport_spi_pump_messages); + drv_data->workqueue = + create_singlethread_workqueue(dev_name(drv_data->master->dev.parent)); + if (drv_data->workqueue == NULL) + return -EBUSY; + + return 0; +} + +static int +bfin_sport_spi_start_queue(struct bfin_sport_spi_master_data *drv_data) +{ + unsigned long flags; + + spin_lock_irqsave(&drv_data->lock, flags); + + if (drv_data->run || drv_data->busy) { + spin_unlock_irqrestore(&drv_data->lock, flags); + return -EBUSY; + } + + drv_data->run = true; + drv_data->cur_msg = NULL; + drv_data->cur_transfer = NULL; + drv_data->cur_chip = NULL; + spin_unlock_irqrestore(&drv_data->lock, flags); + + queue_work(drv_data->workqueue, &drv_data->pump_messages); + + return 0; +} + +static inline int +bfin_sport_spi_stop_queue(struct bfin_sport_spi_master_data *drv_data) +{ + unsigned long flags; + unsigned limit = 500; + int status = 0; + + spin_lock_irqsave(&drv_data->lock, flags); + + /* + * This is a bit lame, but is optimized for the common execution path. + * A wait_queue on the drv_data->busy could be used, but then the common + * execution path (pump_messages) would be required to call wake_up or + * friends on every SPI message. Do this instead + */ + drv_data->run = false; + while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) { + spin_unlock_irqrestore(&drv_data->lock, flags); + msleep(10); + spin_lock_irqsave(&drv_data->lock, flags); + } + + if (!list_empty(&drv_data->queue) || drv_data->busy) + status = -EBUSY; + + spin_unlock_irqrestore(&drv_data->lock, flags); + + return status; +} + +static inline int +bfin_sport_spi_destroy_queue(struct bfin_sport_spi_master_data *drv_data) +{ + int status; + + status = bfin_sport_spi_stop_queue(drv_data); + if (status) + return status; + + destroy_workqueue(drv_data->workqueue); + + return 0; +} + +static int __devinit +bfin_sport_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bfin5xx_spi_master *platform_info; + struct spi_master *master; + struct resource *res, *ires; + struct bfin_sport_spi_master_data *drv_data; + int status; + + platform_info = dev->platform_data; + + /* Allocate master with space for drv_data */ + master = spi_alloc_master(dev, sizeof(*master) + 16); + if (!master) { + dev_err(dev, "cannot alloc spi_master\n"); + return -ENOMEM; + } + + drv_data = spi_master_get_devdata(master); + drv_data->master = master; + drv_data->dev = dev; + drv_data->pin_req = platform_info->pin_req; + + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + master->bus_num = pdev->id; + master->num_chipselect = platform_info->num_chipselect; + master->cleanup = bfin_sport_spi_cleanup; + master->setup = bfin_sport_spi_setup; + master->transfer = bfin_sport_spi_transfer; + + /* Find and map our resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(dev, "cannot get IORESOURCE_MEM\n"); + status = -ENOENT; + goto out_error_get_res; + } + + drv_data->regs = ioremap(res->start, resource_size(res)); + if (drv_data->regs == NULL) { + dev_err(dev, "cannot map registers\n"); + status = -ENXIO; + goto out_error_ioremap; + } + + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!ires) { + dev_err(dev, "cannot get IORESOURCE_IRQ\n"); + status = -ENODEV; + goto out_error_get_ires; + } + drv_data->err_irq = ires->start; + + /* Initial and start queue */ + status = bfin_sport_spi_init_queue(drv_data); + if (status) { + dev_err(dev, "problem initializing queue\n"); + goto out_error_queue_alloc; + } + + status = bfin_sport_spi_start_queue(drv_data); + if (status) { + dev_err(dev, "problem starting queue\n"); + goto out_error_queue_alloc; + } + + status = request_irq(drv_data->err_irq, sport_err_handler, + 0, "sport_spi_err", drv_data); + if (status) { + dev_err(dev, "unable to request sport err irq\n"); + goto out_error_irq; + } + + status = peripheral_request_list(drv_data->pin_req, DRV_NAME); + if (status) { + dev_err(dev, "requesting peripherals failed\n"); + goto out_error_peripheral; + } + + /* Register with the SPI framework */ + platform_set_drvdata(pdev, drv_data); + status = spi_register_master(master); + if (status) { + dev_err(dev, "problem registering spi master\n"); + goto out_error_master; + } + + dev_info(dev, "%s, regs_base@%p\n", DRV_DESC, drv_data->regs); + return 0; + + out_error_master: + peripheral_free_list(drv_data->pin_req); + out_error_peripheral: + free_irq(drv_data->err_irq, drv_data); + out_error_irq: + out_error_queue_alloc: + bfin_sport_spi_destroy_queue(drv_data); + out_error_get_ires: + iounmap(drv_data->regs); + out_error_ioremap: + out_error_get_res: + spi_master_put(master); + + return status; +} + +/* stop hardware and remove the driver */ +static int __devexit +bfin_sport_spi_remove(struct platform_device *pdev) +{ + struct bfin_sport_spi_master_data *drv_data = platform_get_drvdata(pdev); + int status = 0; + + if (!drv_data) + return 0; + + /* Remove the queue */ + status = bfin_sport_spi_destroy_queue(drv_data); + if (status) + return status; + + /* Disable the SSP at the peripheral and SOC level */ + bfin_sport_spi_disable(drv_data); + + /* Disconnect from the SPI framework */ + spi_unregister_master(drv_data->master); + + peripheral_free_list(drv_data->pin_req); + + /* Prevent double remove */ + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int +bfin_sport_spi_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct bfin_sport_spi_master_data *drv_data = platform_get_drvdata(pdev); + int status; + + status = bfin_sport_spi_stop_queue(drv_data); + if (status) + return status; + + /* stop hardware */ + bfin_sport_spi_disable(drv_data); + + return status; +} + +static int +bfin_sport_spi_resume(struct platform_device *pdev) +{ + struct bfin_sport_spi_master_data *drv_data = platform_get_drvdata(pdev); + int status; + + /* Enable the SPI interface */ + bfin_sport_spi_enable(drv_data); + + /* Start the queue running */ + status = bfin_sport_spi_start_queue(drv_data); + if (status) + dev_err(drv_data->dev, "problem resuming queue\n"); + + return status; +} +#else +# define bfin_sport_spi_suspend NULL +# define bfin_sport_spi_resume NULL +#endif + +static struct platform_driver bfin_sport_spi_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = bfin_sport_spi_probe, + .remove = __devexit_p(bfin_sport_spi_remove), + .suspend = bfin_sport_spi_suspend, + .resume = bfin_sport_spi_resume, +}; + +static int __init bfin_sport_spi_init(void) +{ + return platform_driver_register(&bfin_sport_spi_driver); +} +module_init(bfin_sport_spi_init); + +static void __exit bfin_sport_spi_exit(void) +{ + platform_driver_unregister(&bfin_sport_spi_driver); +} +module_exit(bfin_sport_spi_exit); -- cgit v1.2.2 From 8b20c8cb89b733f5a73fad1f7ad7cff8325e0034 Mon Sep 17 00:00:00 2001 From: "Govindraj.R" Date: Wed, 1 Jun 2011 11:31:24 +0530 Subject: spi/omap2: fix uninitialized variable fixes below compilation warning. The variable doesn't actual ever get used uninitialized, but that's no reason to be sloppy. drivers/spi/omap2_mcspi.c: In function 'omap2_mcspi_txrx_dma': drivers/spi/omap2_mcspi.c:301: warning: 'elements' may be used uninitialized in this function Signed-off-by: Govindraj.R [grant.likely: amended description] Signed-off-by: Grant Likely --- drivers/spi/omap2_mcspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index 6f86ba0175ac..969cdd2fe124 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -298,7 +298,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) unsigned int count, c; unsigned long base, tx_reg, rx_reg; int word_len, data_type, element_count; - int elements; + int elements = 0; u32 l; u8 * rx; const u8 * tx; -- cgit v1.2.2 From e3f88ae9960920598cad132c553019ee79ff3aca Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Mon, 13 Jun 2011 16:23:46 +0530 Subject: spi-pl022: Add missing return value update Return error on out of range cpsdvsr value. Acked-by: Linus Walleij Signed-off-by: Virupax Sadashivpetimath Signed-off-by: Grant Likely --- drivers/spi/amba-pl022.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 6a9e58dd36c7..d18ce9e946d8 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1861,6 +1861,7 @@ static int pl022_setup(struct spi_device *spi) } if ((clk_freq.cpsdvsr < CPSDVR_MIN) || (clk_freq.cpsdvsr > CPSDVR_MAX)) { + status = -EINVAL; dev_err(&spi->dev, "cpsdvsr is configured incorrectly\n"); goto err_config_params; -- cgit v1.2.2 From e479c60456ef22b0869432887216186aabaed086 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 17 Jun 2011 04:35:37 -0400 Subject: spi/bfin_spi: fix handling of default bits per word setting The default bits per word setting should be 8 bits, but since most of our devices have been explicitly setting this up, we didn't notice when the default stopped working. At the moment, any default transfers without an explicit bit size setting error out with: bfin-spi bfin-spi.0: transfer: unsupported bits_per_word So in the transfer logic, have a bits_per_word setting of 0 fall into the 8 bit transfer logic. Signed-off-by: Mike Frysinger Signed-off-by: Grant Likely --- drivers/spi/spi_bfin5xx.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index f706dba165cf..cc880c95e7de 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -681,13 +681,14 @@ static void bfin_spi_pump_transfers(unsigned long data) drv_data->cs_change = transfer->cs_change; /* Bits per word setup */ - bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word; - if ((bits_per_word > 0) && (bits_per_word % 16 == 0)) { + bits_per_word = transfer->bits_per_word ? : + message->spi->bits_per_word ? : 8; + if (bits_per_word % 16 == 0) { drv_data->n_bytes = bits_per_word/8; drv_data->len = (transfer->len) >> 1; cr_width = BIT_CTL_WORDSIZE; drv_data->ops = &bfin_bfin_spi_transfer_ops_u16; - } else if ((bits_per_word > 0) && (bits_per_word % 8 == 0)) { + } else if (bits_per_word % 8 == 0) { drv_data->n_bytes = bits_per_word/8; drv_data->len = transfer->len; cr_width = 0; -- cgit v1.2.2 From 3075741417d47cccc890ed30da9ece666006553a Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Tue, 5 Jul 2011 17:14:02 +0900 Subject: spi/s3c64xx: Bug fix for SPI with different FIFO level The existing macro fails for following scenarios. 1) S5P64X0 channel 1 2) S5PV210 channel 1 The FIFO data level supported in the above SoCs either 64 or 256 bytes depending on the channel. Because of this the TX_DONE is the 25 bit in the status register. The existing macro works for the following scenarios 1) S3C6410 all channels 2) S5PC100 all channels The FIFO data level supported in the above SoCs 64 bytes on all the channels. Because of this the TX_DONE is the 21 bit in the status register. So when we use the existing macro for the non-working SoCs it is not anding with the TX_DONE bit for transmission status check. Signed-off-by: Padmavathi Venna Acked-by: Jassi Brar Acked-by: Grant Likely Signed-off-by: Kukjin Kim --- drivers/spi/spi_s3c64xx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c index 795828b90f45..8945e201e42e 100644 --- a/drivers/spi/spi_s3c64xx.c +++ b/drivers/spi/spi_s3c64xx.c @@ -116,9 +116,7 @@ (((i)->fifo_lvl_mask + 1))) \ ? 1 : 0) -#define S3C64XX_SPI_ST_TX_DONE(v, i) ((((v) >> (i)->rx_lvl_offset) & \ - (((i)->fifo_lvl_mask + 1) << 1)) \ - ? 1 : 0) +#define S3C64XX_SPI_ST_TX_DONE(v, i) (((v) & (1 << (i)->tx_st_done)) ? 1 : 0) #define TX_FIFO_LVL(v, i) (((v) >> 6) & (i)->fifo_lvl_mask) #define RX_FIFO_LVL(v, i) (((v) >> (i)->rx_lvl_offset) & (i)->fifo_lvl_mask) -- cgit v1.2.2