diff options
author | Scott Wood <scottwood@freescale.com> | 2014-04-08 01:00:49 (GMT) |
---|---|---|
committer | Scott Wood <scottwood@freescale.com> | 2014-04-08 19:58:35 (GMT) |
commit | 47d2261a3fa71cde24263559a4219a25e50d8c89 (patch) | |
tree | 28774d5b330ccf1b777a3af222d8356918328013 /drivers/spi/spi-mxs.c | |
parent | fb7f27080adc65cd5f341bdf56a1d0c14f316c1b (diff) | |
parent | 5fb9d37f27351e42f002e372074249f92cbdf815 (diff) | |
download | linux-fsl-qoriq-47d2261a3fa71cde24263559a4219a25e50d8c89.tar.xz |
Merge branch 'merge' into sdk-v1.6.x
This reverts v3.13-rc3+ (78fd82238d0e5716) to v3.12, except for
commits which I noticed which appear relevant to the SDK.
Signed-off-by: Scott Wood <scottwood@freescale.com>
Conflicts:
arch/powerpc/include/asm/kvm_host.h
arch/powerpc/kvm/book3s_hv_rmhandlers.S
arch/powerpc/kvm/book3s_interrupts.S
arch/powerpc/kvm/e500.c
arch/powerpc/kvm/e500mc.c
arch/powerpc/sysdev/fsl_soc.h
drivers/Kconfig
drivers/cpufreq/ppc-corenet-cpufreq.c
drivers/dma/fsldma.c
drivers/dma/s3c24xx-dma.c
drivers/misc/Makefile
drivers/mmc/host/sdhci-of-esdhc.c
drivers/mtd/devices/m25p80.c
drivers/net/ethernet/freescale/gianfar.h
drivers/platform/Kconfig
drivers/platform/Makefile
drivers/spi/spi-fsl-espi.c
include/crypto/algapi.h
include/linux/netdev_features.h
include/linux/skbuff.h
include/net/ip.h
net/core/ethtool.c
Diffstat (limited to 'drivers/spi/spi-mxs.c')
-rw-r--r-- | drivers/spi/spi-mxs.c | 197 |
1 files changed, 114 insertions, 83 deletions
diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 3adebfa..de7b114 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -57,53 +57,34 @@ #define SG_MAXLEN 0xff00 -/* - * Flags for txrx functions. More efficient that using an argument register for - * each one. - */ -#define TXRX_WRITE (1<<0) /* This is a write */ -#define TXRX_DEASSERT_CS (1<<1) /* De-assert CS at end of txrx */ - struct mxs_spi { struct mxs_ssp ssp; struct completion c; - unsigned int sck; /* Rate requested (vs actual) */ }; static int mxs_spi_setup_transfer(struct spi_device *dev, - const struct spi_transfer *t) + struct spi_transfer *t) { struct mxs_spi *spi = spi_master_get_devdata(dev->master); struct mxs_ssp *ssp = &spi->ssp; - const unsigned int hz = min(dev->max_speed_hz, t->speed_hz); + uint32_t hz = 0; + hz = dev->max_speed_hz; + if (t && t->speed_hz) + hz = min(hz, t->speed_hz); if (hz == 0) { - dev_err(&dev->dev, "SPI clock rate of zero not allowed\n"); + dev_err(&dev->dev, "Cannot continue with zero clock\n"); return -EINVAL; } - if (hz != spi->sck) { - mxs_ssp_set_clk_rate(ssp, hz); - /* - * Save requested rate, hz, rather than the actual rate, - * ssp->clk_rate. Otherwise we would set the rate every trasfer - * when the actual rate is not quite the same as requested rate. - */ - spi->sck = hz; - /* - * Perhaps we should return an error if the actual clock is - * nowhere close to what was requested? - */ - } - - writel(BM_SSP_CTRL0_LOCK_CS, - ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + mxs_ssp_set_clk_rate(ssp, hz); writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) | - BF_SSP_CTRL1_WORD_LENGTH(BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) | - ((dev->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | - ((dev->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0), - ssp->base + HW_SSP_CTRL1(ssp)); + BF_SSP_CTRL1_WORD_LENGTH + (BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) | + ((dev->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | + ((dev->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0), + ssp->base + HW_SSP_CTRL1(ssp)); writel(0x0, ssp->base + HW_SSP_CMD0); writel(0x0, ssp->base + HW_SSP_CMD1); @@ -113,15 +94,26 @@ static int mxs_spi_setup_transfer(struct spi_device *dev, static int mxs_spi_setup(struct spi_device *dev) { + int err = 0; + if (!dev->bits_per_word) dev->bits_per_word = 8; - return 0; + if (dev->mode & ~(SPI_CPOL | SPI_CPHA)) + return -EINVAL; + + err = mxs_spi_setup_transfer(dev, NULL); + if (err) { + dev_err(&dev->dev, + "Failed to setup transfer, error = %d\n", err); + } + + return err; } -static u32 mxs_spi_cs_to_reg(unsigned cs) +static uint32_t mxs_spi_cs_to_reg(unsigned cs) { - u32 select = 0; + uint32_t select = 0; /* * i.MX28 Datasheet: 17.10.1: HW_SSP_CTRL0 @@ -139,11 +131,43 @@ static u32 mxs_spi_cs_to_reg(unsigned cs) return select; } +static void mxs_spi_set_cs(struct mxs_spi *spi, unsigned cs) +{ + const uint32_t mask = + BM_SSP_CTRL0_WAIT_FOR_CMD | BM_SSP_CTRL0_WAIT_FOR_IRQ; + uint32_t select; + struct mxs_ssp *ssp = &spi->ssp; + + writel(mask, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + select = mxs_spi_cs_to_reg(cs); + writel(select, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); +} + +static inline void mxs_spi_enable(struct mxs_spi *spi) +{ + struct mxs_ssp *ssp = &spi->ssp; + + writel(BM_SSP_CTRL0_LOCK_CS, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + writel(BM_SSP_CTRL0_IGNORE_CRC, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); +} + +static inline void mxs_spi_disable(struct mxs_spi *spi) +{ + struct mxs_ssp *ssp = &spi->ssp; + + writel(BM_SSP_CTRL0_LOCK_CS, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + writel(BM_SSP_CTRL0_IGNORE_CRC, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); +} + static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set) { const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); struct mxs_ssp *ssp = &spi->ssp; - u32 reg; + uint32_t reg; do { reg = readl_relaxed(ssp->base + offset); @@ -176,9 +200,9 @@ static irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static int mxs_spi_txrx_dma(struct mxs_spi *spi, +static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, unsigned char *buf, int len, - unsigned int flags) + int *first, int *last, int write) { struct mxs_ssp *ssp = &spi->ssp; struct dma_async_tx_descriptor *desc = NULL; @@ -187,11 +211,11 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, const int sgs = DIV_ROUND_UP(len, desc_len); int sg_count; int min, ret; - u32 ctrl0; + uint32_t ctrl0; struct page *vm_page; void *sg_buf; struct { - u32 pio[4]; + uint32_t pio[4]; struct scatterlist sg; } *dma_xfer; @@ -202,27 +226,23 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, if (!dma_xfer) return -ENOMEM; - reinit_completion(&spi->c); + INIT_COMPLETION(spi->c); - /* Chip select was already programmed into CTRL0 */ ctrl0 = readl(ssp->base + HW_SSP_CTRL0); - ctrl0 &= ~(BM_SSP_CTRL0_XFER_COUNT | BM_SSP_CTRL0_IGNORE_CRC | - BM_SSP_CTRL0_READ); - ctrl0 |= BM_SSP_CTRL0_DATA_XFER; + ctrl0 &= ~BM_SSP_CTRL0_XFER_COUNT; + ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); - if (!(flags & TXRX_WRITE)) + if (*first) + ctrl0 |= BM_SSP_CTRL0_LOCK_CS; + if (!write) ctrl0 |= BM_SSP_CTRL0_READ; /* Queue the DMA data transfer. */ for (sg_count = 0; sg_count < sgs; sg_count++) { - /* Prepare the transfer descriptor. */ min = min(len, desc_len); - /* - * De-assert CS on last segment if flag is set (i.e., no more - * transfers will follow) - */ - if ((sg_count + 1 == sgs) && (flags & TXRX_DEASSERT_CS)) + /* Prepare the transfer descriptor. */ + if ((sg_count + 1 == sgs) && *last) ctrl0 |= BM_SSP_CTRL0_IGNORE_CRC; if (ssp->devid == IMX23_SSP) { @@ -247,7 +267,7 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, sg_init_one(&dma_xfer[sg_count].sg, sg_buf, min); ret = dma_map_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, - (flags & TXRX_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); len -= min; buf += min; @@ -267,7 +287,7 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, desc = dmaengine_prep_slave_sg(ssp->dmach, &dma_xfer[sg_count].sg, 1, - (flags & TXRX_WRITE) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, + write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { @@ -304,7 +324,7 @@ err_vmalloc: while (--sg_count >= 0) { err_mapped: dma_unmap_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, - (flags & TXRX_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); } kfree(dma_xfer); @@ -312,19 +332,20 @@ err_mapped: return ret; } -static int mxs_spi_txrx_pio(struct mxs_spi *spi, +static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, unsigned char *buf, int len, - unsigned int flags) + int *first, int *last, int write) { struct mxs_ssp *ssp = &spi->ssp; - writel(BM_SSP_CTRL0_IGNORE_CRC, - ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + if (*first) + mxs_spi_enable(spi); + + mxs_spi_set_cs(spi, cs); while (len--) { - if (len == 0 && (flags & TXRX_DEASSERT_CS)) - writel(BM_SSP_CTRL0_IGNORE_CRC, - ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + if (*last && len == 0) + mxs_spi_disable(spi); if (ssp->devid == IMX23_SSP) { writel(BM_SSP_CTRL0_XFER_COUNT, @@ -335,7 +356,7 @@ static int mxs_spi_txrx_pio(struct mxs_spi *spi, writel(1, ssp->base + HW_SSP_XFER_SIZE); } - if (flags & TXRX_WRITE) + if (write) writel(BM_SSP_CTRL0_READ, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); else @@ -348,13 +369,13 @@ static int mxs_spi_txrx_pio(struct mxs_spi *spi, if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 1)) return -ETIMEDOUT; - if (flags & TXRX_WRITE) + if (write) writel(*buf, ssp->base + HW_SSP_DATA(ssp)); writel(BM_SSP_CTRL0_DATA_XFER, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); - if (!(flags & TXRX_WRITE)) { + if (!write) { if (mxs_ssp_wait(spi, HW_SSP_STATUS(ssp), BM_SSP_STATUS_FIFO_EMPTY, 0)) return -ETIMEDOUT; @@ -379,15 +400,14 @@ static int mxs_spi_transfer_one(struct spi_master *master, { struct mxs_spi *spi = spi_master_get_devdata(master); struct mxs_ssp *ssp = &spi->ssp; + int first, last; struct spi_transfer *t, *tmp_t; - unsigned int flag; int status = 0; + int cs; + + first = last = 0; - /* Program CS register bits here, it will be used for all transfers. */ - writel(BM_SSP_CTRL0_WAIT_FOR_CMD | BM_SSP_CTRL0_WAIT_FOR_IRQ, - ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); - writel(mxs_spi_cs_to_reg(m->spi->chip_select), - ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + cs = m->spi->chip_select; list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) { @@ -395,9 +415,16 @@ static int mxs_spi_transfer_one(struct spi_master *master, if (status) break; - /* De-assert on last transfer, inverted by cs_change flag */ - flag = (&t->transfer_list == m->transfers.prev) ^ t->cs_change ? - TXRX_DEASSERT_CS : 0; + if (&t->transfer_list == m->transfers.next) + first = 1; + if (&t->transfer_list == m->transfers.prev) + last = 1; + if ((t->rx_buf && t->tx_buf) || (t->rx_dma && t->tx_dma)) { + dev_err(ssp->dev, + "Cannot send and receive simultaneously\n"); + status = -EINVAL; + break; + } /* * Small blocks can be transfered via PIO. @@ -414,26 +441,26 @@ static int mxs_spi_transfer_one(struct spi_master *master, STMP_OFFSET_REG_CLR); if (t->tx_buf) - status = mxs_spi_txrx_pio(spi, + status = mxs_spi_txrx_pio(spi, cs, (void *)t->tx_buf, - t->len, flag | TXRX_WRITE); + t->len, &first, &last, 1); if (t->rx_buf) - status = mxs_spi_txrx_pio(spi, + status = mxs_spi_txrx_pio(spi, cs, t->rx_buf, t->len, - flag); + &first, &last, 0); } else { writel(BM_SSP_CTRL1_DMA_ENABLE, ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_SET); if (t->tx_buf) - status = mxs_spi_txrx_dma(spi, + status = mxs_spi_txrx_dma(spi, cs, (void *)t->tx_buf, t->len, - flag | TXRX_WRITE); + &first, &last, 1); if (t->rx_buf) - status = mxs_spi_txrx_dma(spi, + status = mxs_spi_txrx_dma(spi, cs, t->rx_buf, t->len, - flag); + &first, &last, 0); } if (status) { @@ -442,6 +469,7 @@ static int mxs_spi_transfer_one(struct spi_master *master, } m->actual_length += t->len; + first = last = 0; } m->status = status; @@ -535,6 +563,7 @@ static int mxs_spi_probe(struct platform_device *pdev) goto out_dma_release; clk_set_rate(ssp->clk, clk_freq); + ssp->clk_rate = clk_get_rate(ssp->clk) / 1000; ret = stmp_reset_block(ssp->base); if (ret) @@ -542,7 +571,7 @@ static int mxs_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); - ret = devm_spi_register_master(&pdev->dev, master); + ret = spi_register_master(master); if (ret) { dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret); goto out_disable_clk; @@ -565,12 +594,14 @@ static int mxs_spi_remove(struct platform_device *pdev) struct mxs_spi *spi; struct mxs_ssp *ssp; - master = platform_get_drvdata(pdev); + master = spi_master_get(platform_get_drvdata(pdev)); spi = spi_master_get_devdata(master); ssp = &spi->ssp; + spi_unregister_master(master); clk_disable_unprepare(ssp->clk); dma_release_channel(ssp->dmach); + spi_master_put(master); return 0; } |