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(-) diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index b3a94ca0a75..d7039279505 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.3-18-g5258 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(-) diff --git a/drivers/spi/orion_spi.c b/drivers/spi/orion_spi.c index 3aea50da7b2..0b677dc041a 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.3-18-g5258 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(-) diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 4c37c4e2864..8cdddc97325 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.3-18-g5258 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(+) diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c index c3038da2648..e7b893f2a21 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.3-18-g5258 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 --- arch/arm/plat-samsung/include/plat/s3c64xx-spi.h | 3 + drivers/spi/spi_s3c64xx.c | 94 ++++++++++++++---------- 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h b/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h index e5aba8f95b7..b226f7405e6 100644 --- a/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h +++ b/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h @@ -32,6 +32,8 @@ struct s3c64xx_spi_csinfo { * struct s3c64xx_spi_info - SPI Controller defining structure * @src_clk_nr: Clock source index for the CLK_CFG[SPI_CLKSEL] field. * @src_clk_name: Platform name of the corresponding clock. + * @clk_from_cmu: If the SPI clock/prescalar control block is present + * by the platform's clock-management-unit and not in SPI controller. * @num_cs: Number of CS this controller emulates. * @cfg_gpio: Configure pins for this SPI controller. * @fifo_lvl_mask: All tx fifo_lvl fields start at offset-6 @@ -41,6 +43,7 @@ struct s3c64xx_spi_csinfo { struct s3c64xx_spi_info { int src_clk_nr; char *src_clk_name; + bool clk_from_cmu; int num_cs; diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c index e7b893f2a21..9e0aa3c7b4d 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.3-18-g5258 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(-) diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c index 9e0aa3c7b4d..795828b90f4 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.3-18-g5258 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 diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 91c2f4f3af1..b32167a686b 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 e9cbd18217a..65a232d50bc 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 00000000000..58b183f6eec --- /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.3-18-g5258 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(-) diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c index 58b183f6eec..97746232741 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_RF