From 646781d3325cdcc648b8eeb3b7cda393bcb78659 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:11 +0200 Subject: spi/mxs: Add SPI driver for mx233/mx28 This is slightly reworked version of the SPI driver. Support for DT has been added and it's been converted to queued API. Based on previous attempt by: Fabio Estevam Signed-off-by: Fabio Estevam Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi-mxs.c | 431 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 439 insertions(+) create mode 100644 drivers/spi/spi-mxs.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5f84b5563c2d..6992b30b4e42 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -364,6 +364,13 @@ config SPI_STMP3XXX help SPI driver for Freescale STMP37xx/378x SoC SSP interface +config SPI_MXS + tristate "Freescale MXS SPI controller" + depends on ARCH_MXS + select STMP_DEVICE + help + SPI driver for Freescale MXS devices. + config SPI_TEGRA tristate "Nvidia Tegra SPI controller" depends on ARCH_TEGRA && (TEGRA_SYSTEM_DMA || TEGRA20_APB_DMA) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3920dcf4c740..3b72d87b3309 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o +obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c new file mode 100644 index 000000000000..7bf826f5af59 --- /dev/null +++ b/drivers/spi/spi-mxs.c @@ -0,0 +1,431 @@ +/* + * Freescale MXS SPI master driver + * + * Copyright 2012 DENX Software Engineering, GmbH. + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * + * Rework and transition to new API by: + * Marek Vasut + * + * Based on previous attempt by: + * Fabio Estevam + * + * Based on code from U-Boot bootloader by: + * Marek Vasut + * + * Based on spi-stmp.c, which is: + * Author: Dmitry Pervushin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "mxs-spi" + +#define SSP_TIMEOUT 1000 /* 1000 ms */ + +struct mxs_spi { + struct mxs_ssp ssp; +}; + +static int mxs_spi_setup_transfer(struct spi_device *dev, + struct spi_transfer *t) +{ + struct mxs_spi *spi = spi_master_get_devdata(dev->master); + struct mxs_ssp *ssp = &spi->ssp; + uint8_t bits_per_word; + uint32_t hz = 0; + + bits_per_word = dev->bits_per_word; + if (t && t->bits_per_word) + bits_per_word = t->bits_per_word; + + if (bits_per_word != 8) { + dev_err(&dev->dev, "%s, unsupported bits_per_word=%d\n", + __func__, bits_per_word); + return -EINVAL; + } + + hz = dev->max_speed_hz; + if (t && t->speed_hz) + hz = min(hz, t->speed_hz); + if (hz == 0) { + dev_err(&dev->dev, "Cannot continue with zero clock\n"); + return -EINVAL; + } + + 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)); + + writel(0x0, ssp->base + HW_SSP_CMD0); + writel(0x0, ssp->base + HW_SSP_CMD1); + + return 0; +} + +static int mxs_spi_setup(struct spi_device *dev) +{ + int err = 0; + + if (!dev->bits_per_word) + dev->bits_per_word = 8; + + 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 uint32_t mxs_spi_cs_to_reg(unsigned cs) +{ + uint32_t select = 0; + + /* + * i.MX28 Datasheet: 17.10.1: HW_SSP_CTRL0 + * + * The bits BM_SSP_CTRL0_WAIT_FOR_CMD and BM_SSP_CTRL0_WAIT_FOR_IRQ + * in HW_SSP_CTRL0 register do have multiple usage, please refer to + * the datasheet for further details. In SPI mode, they are used to + * toggle the chip-select lines (nCS pins). + */ + if (cs & 1) + select |= BM_SSP_CTRL0_WAIT_FOR_CMD; + if (cs & 2) + select |= BM_SSP_CTRL0_WAIT_FOR_IRQ; + + 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) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); + struct mxs_ssp *ssp = &spi->ssp; + uint32_t reg; + + while (1) { + reg = readl_relaxed(ssp->base + offset); + + if (set && ((reg & mask) == mask)) + break; + + if (!set && ((~reg & mask) == mask)) + break; + + udelay(1); + + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + } + return 0; +} + +static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, + unsigned char *buf, int len, + int *first, int *last, int write) +{ + struct mxs_ssp *ssp = &spi->ssp; + + if (*first) + mxs_spi_enable(spi); + + mxs_spi_set_cs(spi, cs); + + while (len--) { + if (*last && len == 0) + mxs_spi_disable(spi); + + if (ssp->devid == IMX23_SSP) { + writel(BM_SSP_CTRL0_XFER_COUNT, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + writel(1, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + } else { + writel(1, ssp->base + HW_SSP_XFER_SIZE); + } + + if (write) + writel(BM_SSP_CTRL0_READ, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + else + writel(BM_SSP_CTRL0_READ, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + + writel(BM_SSP_CTRL0_RUN, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + + if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 1)) + return -ETIMEDOUT; + + 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 (!write) { + if (mxs_ssp_wait(spi, HW_SSP_STATUS(ssp), + BM_SSP_STATUS_FIFO_EMPTY, 0)) + return -ETIMEDOUT; + + *buf = (readl(ssp->base + HW_SSP_DATA(ssp)) & 0xff); + } + + if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 0)) + return -ETIMEDOUT; + + buf++; + } + + if (len <= 0) + return 0; + + return -ETIMEDOUT; +} + +static int mxs_spi_transfer_one(struct spi_master *master, + struct spi_message *m) +{ + struct mxs_spi *spi = spi_master_get_devdata(master); + struct mxs_ssp *ssp = &spi->ssp; + int first, last; + struct spi_transfer *t, *tmp_t; + int status = 0; + int cs; + + first = last = 0; + + cs = m->spi->chip_select; + + list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) { + + status = mxs_spi_setup_transfer(m->spi, t); + if (status) + break; + + 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) { + dev_err(ssp->dev, + "Cannot send and receive simultaneously\n"); + status = -EINVAL; + break; + } + + if (t->tx_buf) + status = mxs_spi_txrx_pio(spi, cs, (void *)t->tx_buf, + t->len, &first, &last, 1); + if (t->rx_buf) + status = mxs_spi_txrx_pio(spi, cs, t->rx_buf, + t->len, &first, &last, 0); + + m->actual_length += t->len; + if (status) + break; + + first = last = 0; + } + + m->status = 0; + spi_finalize_current_message(master); + + return status; +} + +static const struct of_device_id mxs_spi_dt_ids[] = { + { .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, }, + { .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_spi_dt_ids); + +static int __devinit mxs_spi_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(mxs_spi_dt_ids, &pdev->dev); + struct device_node *np = pdev->dev.of_node; + struct spi_master *master; + struct mxs_spi *spi; + struct mxs_ssp *ssp; + struct resource *iores; + struct pinctrl *pinctrl; + struct clk *clk; + void __iomem *base; + int devid; + int ret = 0; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) + return -EINVAL; + + base = devm_request_and_ioremap(&pdev->dev, iores); + if (!base) + return -EADDRNOTAVAIL; + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + return PTR_ERR(pinctrl); + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (np) + devid = (enum mxs_ssp_id) of_id->data; + else + devid = pdev->id_entry->driver_data; + + master = spi_alloc_master(&pdev->dev, sizeof(*spi)); + if (!master) + return -ENOMEM; + + master->transfer_one_message = mxs_spi_transfer_one; + master->setup = mxs_spi_setup; + master->mode_bits = SPI_CPOL | SPI_CPHA; + master->num_chipselect = 3; + master->dev.of_node = np; + master->flags = SPI_MASTER_HALF_DUPLEX; + + spi = spi_master_get_devdata(master); + ssp = &spi->ssp; + ssp->dev = &pdev->dev; + ssp->clk = clk; + ssp->base = base; + ssp->devid = devid; + + clk_prepare_enable(ssp->clk); + ssp->clk_rate = clk_get_rate(ssp->clk) / 1000; + + stmp_reset_block(ssp->base); + + platform_set_drvdata(pdev, master); + + ret = spi_register_master(master); + if (ret) { + dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret); + goto out_master_free; + } + + return 0; + +out_master_free: + platform_set_drvdata(pdev, NULL); + clk_disable_unprepare(ssp->clk); + spi_master_put(master); + return ret; +} + +static int __devexit mxs_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master; + struct mxs_spi *spi; + struct mxs_ssp *ssp; + + master = platform_get_drvdata(pdev); + spi = spi_master_get_devdata(master); + ssp = &spi->ssp; + + spi_unregister_master(master); + + platform_set_drvdata(pdev, NULL); + + clk_disable_unprepare(ssp->clk); + + spi_master_put(master); + + return 0; +} + +static struct platform_driver mxs_spi_driver = { + .probe = mxs_spi_probe, + .remove = __devexit_p(mxs_spi_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = mxs_spi_dt_ids, + }, +}; + +module_platform_driver(mxs_spi_driver); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("MXS SPI master driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mxs-spi"); -- cgit v1.2.3 From 65defb9b3ba67c1d6f88ac62c24644eb23a7b676 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:12 +0200 Subject: mmc: spi: Pull out common DMA parts from MXS MMC These parts will be used by the MXS SPI driver too. Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 7bf826f5af59..0f28afb80310 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 474afc042fb9db8f88b68243f78a38cb764692fc Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:13 +0200 Subject: spi/mxs: Add DMA support into SPI driver Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 216 insertions(+), 15 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 0f28afb80310..130a43688352 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -55,8 +55,12 @@ #define SSP_TIMEOUT 1000 /* 1000 ms */ +#define SG_NUM 4 +#define SG_MAXLEN 0xff00 + struct mxs_spi { struct mxs_ssp ssp; + struct completion c; }; static int mxs_spi_setup_transfer(struct spi_device *dev, @@ -194,6 +198,115 @@ static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set) return 0; } +static void mxs_ssp_dma_irq_callback(void *param) +{ + struct mxs_spi *spi = param; + complete(&spi->c); +} + +static irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id) +{ + struct mxs_ssp *ssp = dev_id; + dev_err(ssp->dev, "%s[%i] CTRL1=%08x STATUS=%08x\n", + __func__, __LINE__, + readl(ssp->base + HW_SSP_CTRL1(ssp)), + readl(ssp->base + HW_SSP_STATUS(ssp))); + return IRQ_HANDLED; +} + +static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, + unsigned char *buf, int len, + int *first, int *last, int write) +{ + struct mxs_ssp *ssp = &spi->ssp; + struct dma_async_tx_descriptor *desc; + struct scatterlist sg[SG_NUM]; + int sg_count; + uint32_t pio = BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); + int ret; + + if (len > SG_NUM * SG_MAXLEN) { + dev_err(ssp->dev, "Data chunk too big for DMA\n"); + return -EINVAL; + } + + init_completion(&spi->c); + + if (*first) + pio |= BM_SSP_CTRL0_LOCK_CS; + if (*last) + pio |= BM_SSP_CTRL0_IGNORE_CRC; + if (!write) + pio |= BM_SSP_CTRL0_READ; + + if (ssp->devid == IMX23_SSP) + pio |= len; + else + writel(len, ssp->base + HW_SSP_XFER_SIZE); + + /* Queue the PIO register write transfer. */ + desc = dmaengine_prep_slave_sg(ssp->dmach, + (struct scatterlist *)&pio, + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(ssp->dev, + "Failed to get PIO reg. write descriptor.\n"); + return -EINVAL; + } + + /* Queue the DMA data transfer. */ + sg_init_table(sg, (len / SG_MAXLEN) + 1); + sg_count = 0; + while (len) { + sg_set_buf(&sg[sg_count++], buf, min(len, SG_MAXLEN)); + len -= min(len, SG_MAXLEN); + buf += min(len, SG_MAXLEN); + } + dma_map_sg(ssp->dev, sg, sg_count, + write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + desc = dmaengine_prep_slave_sg(ssp->dmach, sg, sg_count, + write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (!desc) { + dev_err(ssp->dev, + "Failed to get DMA data write descriptor.\n"); + ret = -EINVAL; + goto err; + } + + /* + * The last descriptor must have this callback, + * to finish the DMA transaction. + */ + desc->callback = mxs_ssp_dma_irq_callback; + desc->callback_param = spi; + + /* Start the transfer. */ + dmaengine_submit(desc); + dma_async_issue_pending(ssp->dmach); + + ret = wait_for_completion_timeout(&spi->c, + msecs_to_jiffies(SSP_TIMEOUT)); + + if (!ret) { + dev_err(ssp->dev, "DMA transfer timeout\n"); + ret = -ETIMEDOUT; + goto err; + } + + ret = 0; + +err: + for (--sg_count; sg_count >= 0; sg_count--) { + dma_unmap_sg(ssp->dev, &sg[sg_count], 1, + write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + } + + return ret; +} + static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, unsigned char *buf, int len, int *first, int *last, int write) @@ -281,19 +394,49 @@ static int mxs_spi_transfer_one(struct spi_master *master, first = 1; if (&t->transfer_list == m->transfers.prev) last = 1; - if (t->rx_buf && t->tx_buf) { + 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; } - if (t->tx_buf) - status = mxs_spi_txrx_pio(spi, cs, (void *)t->tx_buf, - t->len, &first, &last, 1); - if (t->rx_buf) - status = mxs_spi_txrx_pio(spi, cs, t->rx_buf, - t->len, &first, &last, 0); + /* + * Small blocks can be transfered via PIO. + * Measured by empiric means: + * + * dd if=/dev/mtdblock0 of=/dev/null bs=1024k count=1 + * + * DMA only: 2.164808 seconds, 473.0KB/s + * Combined: 1.676276 seconds, 610.9KB/s + */ + if (t->len <= 256) { + writel(BM_SSP_CTRL1_DMA_ENABLE, + ssp->base + HW_SSP_CTRL1(ssp) + + STMP_OFFSET_REG_CLR); + + if (t->tx_buf) + status = mxs_spi_txrx_pio(spi, cs, + (void *)t->tx_buf, + t->len, &first, &last, 1); + if (t->rx_buf) + status = mxs_spi_txrx_pio(spi, cs, + t->rx_buf, t->len, + &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, cs, + (void *)t->tx_buf, t->len, + &first, &last, 1); + if (t->rx_buf) + status = mxs_spi_txrx_dma(spi, cs, + t->rx_buf, t->len, + &first, &last, 0); + } m->actual_length += t->len; if (status) @@ -308,6 +451,21 @@ static int mxs_spi_transfer_one(struct spi_master *master, return status; } +static bool mxs_ssp_dma_filter(struct dma_chan *chan, void *param) +{ + struct mxs_ssp *ssp = param; + + if (!mxs_dma_is_apbh(chan)) + return false; + + if (chan->chan_id != ssp->dma_channel) + return false; + + chan->private = &ssp->dma_data; + + return true; +} + static const struct of_device_id mxs_spi_dt_ids[] = { { .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, }, { .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, }, @@ -323,15 +481,18 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) struct spi_master *master; struct mxs_spi *spi; struct mxs_ssp *ssp; - struct resource *iores; + struct resource *iores, *dmares; struct pinctrl *pinctrl; struct clk *clk; void __iomem *base; - int devid; - int ret = 0; + int devid, dma_channel; + int ret = 0, irq_err, irq_dma; + dma_cap_mask_t mask; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) + irq_err = platform_get_irq(pdev, 0); + irq_dma = platform_get_irq(pdev, 1); + if (!iores || irq_err < 0 || irq_dma < 0) return -EINVAL; base = devm_request_and_ioremap(&pdev->dev, iores); @@ -346,10 +507,26 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - if (np) + if (np) { devid = (enum mxs_ssp_id) of_id->data; - else + /* + * TODO: This is a temporary solution and should be changed + * to use generic DMA binding later when the helpers get in. + */ + ret = of_property_read_u32(np, "fsl,ssp-dma-channel", + &dma_channel); + if (ret) { + dev_err(&pdev->dev, + "Failed to get DMA channel\n"); + return -EINVAL; + } + } else { + dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!dmares) + return -EINVAL; devid = pdev->id_entry->driver_data; + dma_channel = dmares->start; + } master = spi_alloc_master(&pdev->dev, sizeof(*spi)); if (!master) @@ -368,8 +545,28 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) ssp->clk = clk; ssp->base = base; ssp->devid = devid; + ssp->dma_channel = dma_channel; + + ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0, + DRIVER_NAME, ssp); + if (ret) + goto out_master_free; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + ssp->dma_data.chan_irq = irq_dma; + ssp->dmach = dma_request_channel(mask, mxs_ssp_dma_filter, ssp); + if (!ssp->dmach) { + dev_err(ssp->dev, "Failed to request DMA\n"); + goto out_master_free; + } + /* + * Crank up the clock to 120MHz, this will be further divided onto a + * proper speed. + */ clk_prepare_enable(ssp->clk); + clk_set_rate(ssp->clk, 120 * 1000 * 1000); ssp->clk_rate = clk_get_rate(ssp->clk) / 1000; stmp_reset_block(ssp->base); @@ -379,14 +576,16 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) ret = spi_register_master(master); if (ret) { dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret); - goto out_master_free; + goto out_free_dma; } return 0; -out_master_free: +out_free_dma: platform_set_drvdata(pdev, NULL); + dma_release_channel(ssp->dmach); clk_disable_unprepare(ssp->clk); +out_master_free: spi_master_put(master); return ret; } @@ -405,6 +604,8 @@ static int __devexit mxs_spi_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); + dma_release_channel(ssp->dmach); + clk_disable_unprepare(ssp->clk); spi_master_put(master); -- cgit v1.2.3 From ba59a807883382f6ce5ad884312d91d5748e90ee Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 23 Jul 2012 13:16:55 +0200 Subject: spi: Refactor spi-orion to use SPI framework queue. Replace the deprecated master->transfer with transfer_one_message() and allow the SPI subsystem handle all the queuing of messages. Signed-off-by: Andrew Lunn Acked-by: Linus Walleij Acked-by: Sebastian Hesselbarth Signed-off-by: Mark Brown --- drivers/spi/spi-orion.c | 209 ++++++++++++++---------------------------------- 1 file changed, 61 insertions(+), 148 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 9b0caddce503..b17c09cf0a05 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -36,12 +36,6 @@ #define ORION_SPI_CLK_PRESCALE_MASK 0x1F struct orion_spi { - struct work_struct work; - - /* Lock access to transfer list. */ - spinlock_t lock; - - struct list_head msg_queue; struct spi_master *master; void __iomem *base; unsigned int max_speed; @@ -49,8 +43,6 @@ struct orion_spi { struct clk *clk; }; -static struct workqueue_struct *orion_spi_wq; - static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) { return orion_spi->base + reg; @@ -277,73 +269,78 @@ out: } -static void orion_spi_work(struct work_struct *work) +static int orion_spi_transfer_one_message(struct spi_master *master, + struct spi_message *m) { - struct orion_spi *orion_spi = - container_of(work, struct orion_spi, work); - - spin_lock_irq(&orion_spi->lock); - while (!list_empty(&orion_spi->msg_queue)) { - struct spi_message *m; - struct spi_device *spi; - struct spi_transfer *t = NULL; - int par_override = 0; - int status = 0; - int cs_active = 0; - - m = container_of(orion_spi->msg_queue.next, struct spi_message, - queue); + struct orion_spi *orion_spi = spi_master_get_devdata(master); + struct spi_device *spi = m->spi; + struct spi_transfer *t = NULL; + int par_override = 0; + int status = 0; + int cs_active = 0; - list_del_init(&m->queue); - spin_unlock_irq(&orion_spi->lock); + /* Load defaults */ + status = orion_spi_setup_transfer(spi, NULL); - spi = m->spi; + if (status < 0) + goto msg_done; - /* Load defaults */ - status = orion_spi_setup_transfer(spi, NULL); + list_for_each_entry(t, &m->transfers, transfer_list) { + /* make sure buffer length is even when working in 16 + * bit mode*/ + 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", + t->len); + status = -EIO; + goto msg_done; + } - if (status < 0) + if (t->speed_hz && t->speed_hz < orion_spi->min_speed) { + dev_err(&spi->dev, + "message rejected : " + "device min speed (%d Hz) exceeds " + "required transfer speed (%d Hz)\n", + orion_spi->min_speed, t->speed_hz); + status = -EIO; goto msg_done; + } - list_for_each_entry(t, &m->transfers, transfer_list) { - if (par_override || t->speed_hz || t->bits_per_word) { - par_override = 1; - status = orion_spi_setup_transfer(spi, t); - if (status < 0) - break; - if (!t->speed_hz && !t->bits_per_word) - par_override = 0; - } - - if (!cs_active) { - orion_spi_set_cs(orion_spi, 1); - cs_active = 1; - } - - if (t->len) - m->actual_length += - orion_spi_write_read(spi, t); - - if (t->delay_usecs) - udelay(t->delay_usecs); - - if (t->cs_change) { - orion_spi_set_cs(orion_spi, 0); - cs_active = 0; - } + if (par_override || t->speed_hz || t->bits_per_word) { + par_override = 1; + status = orion_spi_setup_transfer(spi, t); + if (status < 0) + break; + if (!t->speed_hz && !t->bits_per_word) + par_override = 0; } -msg_done: - if (cs_active) - orion_spi_set_cs(orion_spi, 0); + if (!cs_active) { + orion_spi_set_cs(orion_spi, 1); + cs_active = 1; + } - m->status = status; - m->complete(m->context); + if (t->len) + m->actual_length += orion_spi_write_read(spi, t); - spin_lock_irq(&orion_spi->lock); + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (t->cs_change) { + orion_spi_set_cs(orion_spi, 0); + cs_active = 0; + } } - spin_unlock_irq(&orion_spi->lock); +msg_done: + if (cs_active) + orion_spi_set_cs(orion_spi, 0); + + m->status = status; + spi_finalize_current_message(master); + + return 0; } static int __init orion_spi_reset(struct orion_spi *orion_spi) @@ -376,75 +373,6 @@ static int orion_spi_setup(struct spi_device *spi) return 0; } -static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m) -{ - struct orion_spi *orion_spi; - struct spi_transfer *t = NULL; - unsigned long flags; - - m->actual_length = 0; - m->status = 0; - - /* reject invalid messages and transfers */ - if (list_empty(&m->transfers) || !m->complete) - return -EINVAL; - - orion_spi = spi_master_get_devdata(spi->master); - - list_for_each_entry(t, &m->transfers, transfer_list) { - unsigned int bits_per_word = spi->bits_per_word; - - if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { - dev_err(&spi->dev, - "message rejected : " - "invalid transfer data buffers\n"); - goto msg_rejected; - } - - if (t->bits_per_word) - bits_per_word = t->bits_per_word; - - if ((bits_per_word != 8) && (bits_per_word != 16)) { - dev_err(&spi->dev, - "message rejected : " - "invalid transfer bits_per_word (%d bits)\n", - bits_per_word); - goto msg_rejected; - } - /*make sure buffer length is even when working in 16 bit mode*/ - 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", - t->len); - goto msg_rejected; - } - - if (t->speed_hz && t->speed_hz < orion_spi->min_speed) { - dev_err(&spi->dev, - "message rejected : " - "device min speed (%d Hz) exceeds " - "required transfer speed (%d Hz)\n", - orion_spi->min_speed, t->speed_hz); - goto msg_rejected; - } - } - - - spin_lock_irqsave(&orion_spi->lock, flags); - list_add_tail(&m->queue, &orion_spi->msg_queue); - queue_work(orion_spi_wq, &orion_spi->work); - spin_unlock_irqrestore(&orion_spi->lock, flags); - - return 0; -msg_rejected: - /* Message rejected and not queued */ - m->status = -EINVAL; - if (m->complete) - m->complete(m->context); - return -EINVAL; -} - static int __init orion_spi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -474,7 +402,7 @@ static int __init orion_spi_probe(struct platform_device *pdev) master->mode_bits = 0; master->setup = orion_spi_setup; - master->transfer = orion_spi_transfer; + master->transfer_one_message = orion_spi_transfer_one_message; master->num_chipselect = ORION_NUM_CHIPSELECTS; dev_set_drvdata(&pdev->dev, master); @@ -507,11 +435,6 @@ static int __init orion_spi_probe(struct platform_device *pdev) } spi->base = ioremap(r->start, SZ_1K); - INIT_WORK(&spi->work, orion_spi_work); - - spin_lock_init(&spi->lock); - INIT_LIST_HEAD(&spi->msg_queue); - if (orion_spi_reset(spi) < 0) goto out_rel_mem; @@ -536,14 +459,12 @@ out: static int __exit orion_spi_remove(struct platform_device *pdev) { struct spi_master *master; - struct orion_spi *spi; struct resource *r; + struct orion_spi *spi; master = dev_get_drvdata(&pdev->dev); spi = spi_master_get_devdata(master); - cancel_work_sync(&spi->work); - clk_disable_unprepare(spi->clk); clk_put(spi->clk); @@ -574,21 +495,13 @@ static struct platform_driver orion_spi_driver = { static int __init orion_spi_init(void) { - orion_spi_wq = create_singlethread_workqueue( - orion_spi_driver.driver.name); - if (orion_spi_wq == NULL) - return -ENOMEM; - return platform_driver_probe(&orion_spi_driver, orion_spi_probe); } module_init(orion_spi_init); static void __exit orion_spi_exit(void) { - flush_workqueue(orion_spi_wq); platform_driver_unregister(&orion_spi_driver); - - destroy_workqueue(orion_spi_wq); } module_exit(orion_spi_exit); -- cgit v1.2.3 From 89f98dc5eaf78a5a6920c54ee8ef8992d35f8c48 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 15 Aug 2012 09:30:28 +0200 Subject: spi: spi-altera: Use of_match_ptr Instead of having to define the match table to NULL if CONFIG_OF isn't set, use the of_match_ptr() macro which will do this for us. Signed-off-by: Tobias Klauser Acked-by: Rob Herring Signed-off-by: Mark Brown --- drivers/spi/spi-altera.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index c00d00e96ee4..f1fec2a19d10 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -307,8 +307,6 @@ static const struct of_device_id altera_spi_match[] = { {}, }; 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 = { @@ -318,7 +316,7 @@ static struct platform_driver altera_spi_driver = { .name = DRV_NAME, .owner = THIS_MODULE, .pm = NULL, - .of_match_table = altera_spi_match, + .of_match_table = of_match_ptr(altera_spi_match), }, }; module_platform_driver(altera_spi_driver); -- cgit v1.2.3 From a8e8f1398e80335b923fe747eab2b7c40d14ae31 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Thu, 16 Aug 2012 20:49:30 +0530 Subject: spi: omap2-mcspi: Remove the call to platform_set_drvdata(pdev, NULL) Remove the call of platform_set_drvdata(pdev, NULL) as they are not needed anymore. Signed-off-by: Shubhrajyoti D Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index bc4778175e34..84aeaf0e5a0f 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1240,7 +1240,6 @@ dma_chnl_free: kfree(mcspi->dma_channels); free_master: kfree(master); - platform_set_drvdata(pdev, NULL); return status; } @@ -1259,7 +1258,6 @@ static int __devexit omap2_mcspi_remove(struct platform_device *pdev) spi_unregister_master(master); kfree(dma_channels); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From b2af045c70aa60f7231b22a363d74a1ab5a6297b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 22 Aug 2012 13:42:47 +0200 Subject: drivers/spi/spi-s3c24xx.c: fix error return code Initialize return variable before exiting on an error path. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // ( if@p1 (\(ret < 0\|ret != 0\)) { ... return ret; } | ret@p1 = 0 ) ... when != ret = e1 when != &ret *if(...) { ... when != ret = e2 when forall return ret; } // Signed-off-by: Julia Lawall Signed-off-by: Mark Brown --- drivers/spi/spi-s3c24xx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index 8ee7d790ce49..a2a080b7f42b 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -611,6 +611,7 @@ static int __devinit s3c24xx_spi_probe(struct platform_device *pdev) if (!pdata->set_cs) { if (pdata->pin_cs < 0) { dev_err(&pdev->dev, "No chipselect pin\n"); + err = -EINVAL; goto err_register; } -- cgit v1.2.3 From af4e944dbda418034d1b08628b10e753369fbdc7 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Wed, 22 Aug 2012 11:35:13 +0530 Subject: spi: omap2-mcspi: Remove the macro MOD_REG_BIT Remove the macro MOD_REG_BIT instead make the bit field modifications directly. This deletes a branch operation in cases where the the set is predecided. While at it optimise two sequential bit clear in one step. Acked-by: Felipe Balbi Signed-off-by: Shubhrajyoti D Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 84aeaf0e5a0f..cd87f90588e7 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -140,13 +140,6 @@ struct omap2_mcspi_cs { u32 chconf0; }; -#define MOD_REG_BIT(val, mask, set) do { \ - if (set) \ - val |= mask; \ - else \ - val &= ~mask; \ -} while (0) - static inline void mcspi_write_reg(struct spi_master *master, int idx, u32 val) { @@ -205,7 +198,11 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi, else rw = OMAP2_MCSPI_CHCONF_DMAW; - MOD_REG_BIT(l, rw, enable); + if (enable) + l |= rw; + else + l &= ~rw; + mcspi_write_chconf0(spi, l); } @@ -224,7 +221,11 @@ static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) u32 l; l = mcspi_cached_chconf0(spi); - MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active); + if (cs_active) + l |= OMAP2_MCSPI_CHCONF_FORCE; + else + l &= ~OMAP2_MCSPI_CHCONF_FORCE; + mcspi_write_chconf0(spi, l); } @@ -239,9 +240,8 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master) * to single-channel master mode */ l = mcspi_read_reg(master, OMAP2_MCSPI_MODULCTRL); - MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_STEST, 0); - MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0); - MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1); + l &= ~(OMAP2_MCSPI_MODULCTRL_STEST | OMAP2_MCSPI_MODULCTRL_MS); + l |= OMAP2_MCSPI_MODULCTRL_SINGLE; mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l); ctx->modulctrl = l; @@ -1285,9 +1285,9 @@ static int omap2_mcspi_resume(struct device *dev) * We need to toggle CS state for OMAP take this * change in account. */ - MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 1); + cs->chconf0 |= OMAP2_MCSPI_CHCONF_FORCE; __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); - MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 0); + cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE; __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); } } -- cgit v1.2.3 From 034d3dc9886054384d2a64f2c954ecbb11ce88c8 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Wed, 22 Aug 2012 11:35:12 +0530 Subject: spi: omap2-mcspi: Call pm_runtime_* functions directly Call the pm_runtime functions directly making room for possible pm optimisations. Also the runtime functions aren't just about enabling and disabling of clocks though it does enable clocks also. Acked-by: Felipe Balbi Signed-off-by: Shubhrajyoti D Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index cd87f90588e7..9340974404c4 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -260,16 +260,6 @@ static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) list_for_each_entry(cs, &ctx->cs, node) __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); } -static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi) -{ - pm_runtime_mark_last_busy(mcspi->dev); - pm_runtime_put_autosuspend(mcspi->dev); -} - -static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi) -{ - return pm_runtime_get_sync(mcspi->dev); -} static int omap2_prepare_transfer(struct spi_master *master) { @@ -848,12 +838,13 @@ static int omap2_mcspi_setup(struct spi_device *spi) return ret; } - ret = omap2_mcspi_enable_clocks(mcspi); + ret = pm_runtime_get_sync(mcspi->dev); if (ret < 0) return ret; ret = omap2_mcspi_setup_transfer(spi, NULL); - omap2_mcspi_disable_clocks(mcspi); + pm_runtime_mark_last_busy(mcspi->dev); + pm_runtime_put_autosuspend(mcspi->dev); return ret; } @@ -1067,7 +1058,7 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) struct omap2_mcspi_regs *ctx = &mcspi->ctx; int ret = 0; - ret = omap2_mcspi_enable_clocks(mcspi); + ret = pm_runtime_get_sync(mcspi->dev); if (ret < 0) return ret; @@ -1076,7 +1067,8 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN; omap2_mcspi_set_master_mode(master); - omap2_mcspi_disable_clocks(mcspi); + pm_runtime_mark_last_busy(mcspi->dev); + pm_runtime_put_autosuspend(mcspi->dev); return 0; } @@ -1253,7 +1245,8 @@ static int __devexit omap2_mcspi_remove(struct platform_device *pdev) mcspi = spi_master_get_devdata(master); dma_channels = mcspi->dma_channels; - omap2_mcspi_disable_clocks(mcspi); + pm_runtime_mark_last_busy(mcspi->dev); + pm_runtime_put_autosuspend(mcspi->dev); pm_runtime_disable(&pdev->dev); spi_unregister_master(master); @@ -1278,7 +1271,7 @@ static int omap2_mcspi_resume(struct device *dev) struct omap2_mcspi_regs *ctx = &mcspi->ctx; struct omap2_mcspi_cs *cs; - omap2_mcspi_enable_clocks(mcspi); + pm_runtime_get_sync(mcspi->dev); list_for_each_entry(cs, &ctx->cs, node) { if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) { /* @@ -1291,7 +1284,8 @@ static int omap2_mcspi_resume(struct device *dev) __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); } } - omap2_mcspi_disable_clocks(mcspi); + pm_runtime_mark_last_busy(mcspi->dev); + pm_runtime_put_autosuspend(mcspi->dev); return 0; } #else -- cgit v1.2.3 From a93a2029de7c7f15eb33331cd0f8114892eaa0f3 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Wed, 22 Aug 2012 11:35:14 +0530 Subject: spi: omap2-mcspi: At remove dont use the runtime_autosuspend calls At remove we shouldnt be using the autosuspend timeout as we are calling pm_runtime_disable immediately after. Acked-by: Felipe Balbi Signed-off-by: Shubhrajyoti D Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 9340974404c4..164c15d6a1bb 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1245,8 +1245,7 @@ static int __devexit omap2_mcspi_remove(struct platform_device *pdev) mcspi = spi_master_get_devdata(master); dma_channels = mcspi->dma_channels; - pm_runtime_mark_last_busy(mcspi->dev); - pm_runtime_put_autosuspend(mcspi->dev); + pm_runtime_put_sync(mcspi->dev); pm_runtime_disable(&pdev->dev); spi_unregister_master(master); -- cgit v1.2.3 From 495e3ff365425f9b69884ddde36047184f37c2bd Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 19 Aug 2012 10:44:17 -0700 Subject: spi/pl022: Fix device remove function The call sequence spi_alloc_master/spi_register_master/spi_unregister_master is complete; it reduces the device reference count to zero, which results in device memory being freed. An extra call to spi_master_put is unnecessary and results in an access to free memory. Drop it. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index aab518ec2bbc..47c6753fb31b 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2171,7 +2171,6 @@ pl022_remove(struct amba_device *adev) amba_release_regions(adev); tasklet_disable(&pl022->pump_transfers); spi_unregister_master(pl022->master); - spi_master_put(pl022->master); amba_set_drvdata(adev, NULL); return 0; } -- cgit v1.2.3 From 866c0f25d2dc896e647970cf5e371b5083a6e5d5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Aug 2012 09:29:21 -0700 Subject: spi/mpc52xx: Fix error handling in probe function The call to spi_master_put() is needed to free device memory. It must be called after spi_alloc_master, and must only be called after the device memory is no longer used. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-mpc52xx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index cb3a3830b0a5..a749589677be 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -454,7 +454,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op) GFP_KERNEL); if (!ms->gpio_cs) { rc = -ENOMEM; - goto err_alloc; + goto err_alloc_gpio; } for (i = 0; i < ms->gpio_cs_count; i++) { @@ -514,12 +514,13 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op) err_register: dev_err(&ms->master->dev, "initialization failed\n"); - spi_master_put(master); err_gpio: while (i-- > 0) gpio_free(ms->gpio_cs[i]); kfree(ms->gpio_cs); + err_alloc_gpio: + spi_master_put(master); err_alloc: err_init: iounmap(regs); -- cgit v1.2.3 From f95e1028e3317a084af955aa0b1c25a6c9213869 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Aug 2012 09:29:22 -0700 Subject: spi/mpc52xx: Fix device remove function The call sequence spi_alloc_master/spi_register_master/spi_unregister_master is complete; it reduces the device reference count to zero, which results in device memory being freed. An extra call to spi_master_put is unnecessary and results in an access to free memory. At the same time, since the call to spi_unregister_master results in device memory being freed, it must no longer be accessed afterwards. To fix both problems, call spi_master_get to get an extra reference to the device, and call spi_master_put only after the last access to device data. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-mpc52xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index a749589677be..045410650212 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -529,7 +529,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op) static int __devexit mpc52xx_spi_remove(struct platform_device *op) { - struct spi_master *master = dev_get_drvdata(&op->dev); + struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev)); struct mpc52xx_spi *ms = spi_master_get_devdata(master); int i; @@ -541,8 +541,8 @@ static int __devexit mpc52xx_spi_remove(struct platform_device *op) kfree(ms->gpio_cs); spi_unregister_master(master); - spi_master_put(master); iounmap(ms->regs); + spi_master_put(master); return 0; } -- cgit v1.2.3 From c8c87c656f95d764eb2b460b46b0144794108da4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Aug 2012 09:29:23 -0700 Subject: spi/mpc52xx-psc: Avoid access to freed memory in device remove function The call to spi_unregister_master() in the device remove function frees device memory, and with it any device local data. However, device local data is still accessed after the call to spi_unregister_master(). Acquire a reference to the SPI master device and release it after cleanup is complete to solve the problem. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-mpc52xx-psc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c index 66047156d90d..bd47d262d53f 100644 --- a/drivers/spi/spi-mpc52xx-psc.c +++ b/drivers/spi/spi-mpc52xx-psc.c @@ -481,7 +481,7 @@ static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op) static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op) { - struct spi_master *master = dev_get_drvdata(&op->dev); + struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev)); struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); flush_workqueue(mps->workqueue); @@ -490,6 +490,7 @@ static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op) free_irq(mps->irq, mps); if (mps->psc) iounmap(mps->psc); + spi_master_put(master); return 0; } -- cgit v1.2.3 From 21879213652aca6e3fe250fc8e02c68e71c5e18a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Aug 2012 09:29:24 -0700 Subject: spi/mpc512x-psc: Avoid access to freed memory in device remove function The call to spi_unregister_master() in the device remove function frees device memory, and with it any device local data. However, device local data is still accessed after the call to spi_unregister_master(). Acquire a reference to the SPI master device and release it after cleanup is complete to solve the problem. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-mpc512x-psc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 4c63f772780a..0a1e39e94d06 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -494,7 +494,7 @@ free_master: static int __devexit mpc512x_psc_spi_do_remove(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); + struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); flush_workqueue(mps->workqueue); @@ -503,6 +503,7 @@ static int __devexit mpc512x_psc_spi_do_remove(struct device *dev) free_irq(mps->irq, mps); if (mps->psc) iounmap(mps->psc); + spi_master_put(master); return 0; } -- cgit v1.2.3 From 3ce8859e2e72713d3619285cab609d05c3591fc4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Aug 2012 09:06:27 -0700 Subject: spi: Master driver for NXP SC18IS602/603 This driver adds support for NXP SC18IS602/603 I2C to SPI bus bridge. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi-sc18is602.c | 364 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 371 insertions(+) create mode 100644 drivers/spi/spi-sc18is602.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5f84b5563c2d..920bb4d22d40 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -325,6 +325,12 @@ config SPI_S3C64XX help SPI driver for Samsung S3C64XX and newer SoCs. +config SPI_SC18IS602 + tristate "NXP SC18IS602/602B/603 I2C to SPI bridge" + depends on I2C + help + SPI driver for NXP SC18IS602/602B/603 I2C to SPI bridge. + config SPI_SH_MSIOF tristate "SuperH MSIOF SPI controller" depends on SUPERH && HAVE_CLK diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3920dcf4c740..7559c984db77 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o spi-s3c24xx-hw-y := spi-s3c24xx.o spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o +obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o obj-$(CONFIG_SPI_SH) += spi-sh.o obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c new file mode 100644 index 000000000000..dd9896423f0b --- /dev/null +++ b/drivers/spi/spi-sc18is602.c @@ -0,0 +1,364 @@ +/* + * NXP SC18IS602/603 SPI driver + * + * Copyright (C) Guenter Roeck + * + * 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 +#include +#include + +enum chips { sc18is602, sc18is602b, sc18is603 }; + +#define SC18IS602_BUFSIZ 200 +#define SC18IS602_CLOCK 7372000 + +#define SC18IS602_MODE_CPHA BIT(2) +#define SC18IS602_MODE_CPOL BIT(3) +#define SC18IS602_MODE_LSB_FIRST BIT(5) +#define SC18IS602_MODE_CLOCK_DIV_4 0x0 +#define SC18IS602_MODE_CLOCK_DIV_16 0x1 +#define SC18IS602_MODE_CLOCK_DIV_64 0x2 +#define SC18IS602_MODE_CLOCK_DIV_128 0x3 + +struct sc18is602 { + struct spi_master *master; + struct device *dev; + u8 ctrl; + u32 freq; + u32 speed; + + /* I2C data */ + struct i2c_client *client; + enum chips id; + u8 buffer[SC18IS602_BUFSIZ + 1]; + int tlen; /* Data queued for tx in buffer */ + int rindex; /* Receive data index in buffer */ +}; + +static int sc18is602_wait_ready(struct sc18is602 *hw, int len) +{ + int i, err; + int usecs = 1000000 * len / hw->speed + 1; + u8 dummy[1]; + + for (i = 0; i < 10; i++) { + err = i2c_master_recv(hw->client, dummy, 1); + if (err >= 0) + return 0; + usleep_range(usecs, usecs * 2); + } + return -ETIMEDOUT; +} + +static int sc18is602_txrx(struct sc18is602 *hw, struct spi_message *msg, + struct spi_transfer *t, bool do_transfer) +{ + unsigned int len = t->len; + int ret; + + if (hw->tlen == 0) { + /* First byte (I2C command) is chip select */ + hw->buffer[0] = 1 << msg->spi->chip_select; + hw->tlen = 1; + hw->rindex = 0; + } + /* + * We can not immediately send data to the chip, since each I2C message + * resembles a full SPI message (from CS active to CS inactive). + * Enqueue messages up to the first read or until do_transfer is true. + */ + if (t->tx_buf) { + memcpy(&hw->buffer[hw->tlen], t->tx_buf, len); + hw->tlen += len; + if (t->rx_buf) + do_transfer = true; + else + hw->rindex = hw->tlen - 1; + } else if (t->rx_buf) { + /* + * For receive-only transfers we still need to perform a dummy + * write to receive data from the SPI chip. + * Read data starts at the end of transmit data (minus 1 to + * account for CS). + */ + hw->rindex = hw->tlen - 1; + memset(&hw->buffer[hw->tlen], 0, len); + hw->tlen += len; + do_transfer = true; + } + + if (do_transfer && hw->tlen > 1) { + ret = sc18is602_wait_ready(hw, SC18IS602_BUFSIZ); + if (ret < 0) + return ret; + ret = i2c_master_send(hw->client, hw->buffer, hw->tlen); + if (ret < 0) + return ret; + if (ret != hw->tlen) + return -EIO; + + if (t->rx_buf) { + int rlen = hw->rindex + len; + + ret = sc18is602_wait_ready(hw, hw->tlen); + if (ret < 0) + return ret; + ret = i2c_master_recv(hw->client, hw->buffer, rlen); + if (ret < 0) + return ret; + if (ret != rlen) + return -EIO; + memcpy(t->rx_buf, &hw->buffer[hw->rindex], len); + } + hw->tlen = 0; + } + return len; +} + +static int sc18is602_setup_transfer(struct sc18is602 *hw, u32 hz, u8 mode) +{ + u8 ctrl = 0; + int ret; + + if (mode & SPI_CPHA) + ctrl |= SC18IS602_MODE_CPHA; + if (mode & SPI_CPOL) + ctrl |= SC18IS602_MODE_CPOL; + if (mode & SPI_LSB_FIRST) + ctrl |= SC18IS602_MODE_LSB_FIRST; + + /* Find the closest clock speed */ + if (hz >= hw->freq / 4) { + ctrl |= SC18IS602_MODE_CLOCK_DIV_4; + hw->speed = hw->freq / 4; + } else if (hz >= hw->freq / 16) { + ctrl |= SC18IS602_MODE_CLOCK_DIV_16; + hw->speed = hw->freq / 16; + } else if (hz >= hw->freq / 64) { + ctrl |= SC18IS602_MODE_CLOCK_DIV_64; + hw->speed = hw->freq / 64; + } else { + ctrl |= SC18IS602_MODE_CLOCK_DIV_128; + hw->speed = hw->freq / 128; + } + + /* + * Don't do anything if the control value did not change. The initial + * value of 0xff for hw->ctrl ensures that the correct mode will be set + * with the first call to this function. + */ + if (ctrl == hw->ctrl) + return 0; + + ret = i2c_smbus_write_byte_data(hw->client, 0xf0, ctrl); + if (ret < 0) + return ret; + + hw->ctrl = ctrl; + + return 0; +} + +static int sc18is602_check_transfer(struct spi_device *spi, + struct spi_transfer *t, int tlen) +{ + int bpw; + uint32_t hz; + + if (t && t->len + tlen > SC18IS602_BUFSIZ) + return -EINVAL; + + bpw = spi->bits_per_word; + if (t && t->bits_per_word) + bpw = t->bits_per_word; + if (bpw != 8) + return -EINVAL; + + hz = spi->max_speed_hz; + if (t && t->speed_hz) + hz = t->speed_hz; + if (hz == 0) + return -EINVAL; + + return 0; +} + +static int sc18is602_transfer_one(struct spi_master *master, + struct spi_message *m) +{ + struct sc18is602 *hw = spi_master_get_devdata(master); + struct spi_device *spi = m->spi; + struct spi_transfer *t; + int status = 0; + + /* SC18IS602 does not support CS2 */ + if (hw->id == sc18is602 && spi->chip_select == 2) { + status = -ENXIO; + goto error; + } + + hw->tlen = 0; + list_for_each_entry(t, &m->transfers, transfer_list) { + u32 hz = t->speed_hz ? : spi->max_speed_hz; + bool do_transfer; + + status = sc18is602_check_transfer(spi, t, hw->tlen); + if (status < 0) + break; + + status = sc18is602_setup_transfer(hw, hz, spi->mode); + if (status < 0) + break; + + do_transfer = t->cs_change || list_is_last(&t->transfer_list, + &m->transfers); + + if (t->len) { + status = sc18is602_txrx(hw, m, t, do_transfer); + if (status < 0) + break; + m->actual_length += status; + } + status = 0; + + if (t->delay_usecs) + udelay(t->delay_usecs); + } +error: + m->status = status; + spi_finalize_current_message(master); + + return status; +} + +static int sc18is602_setup(struct spi_device *spi) +{ + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + if (spi->mode & ~(SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST)) + return -EINVAL; + + return sc18is602_check_transfer(spi, NULL, 0); +} + +static int sc18is602_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *np = dev->of_node; + struct sc18is602_platform_data *pdata = dev_get_platdata(dev); + struct sc18is602 *hw; + struct spi_master *master; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + master = spi_alloc_master(dev, sizeof(struct sc18is602)); + if (!master) + return -ENOMEM; + + hw = spi_master_get_devdata(master); + i2c_set_clientdata(client, hw); + + hw->master = master; + hw->client = client; + hw->dev = dev; + hw->ctrl = 0xff; + + hw->id = id->driver_data; + + switch (hw->id) { + case sc18is602: + case sc18is602b: + master->num_chipselect = 4; + hw->freq = SC18IS602_CLOCK; + break; + case sc18is603: + master->num_chipselect = 2; + if (pdata) { + hw->freq = pdata->clock_frequency; + } else { + const __be32 *val; + int len; + + val = of_get_property(np, "clock-frequency", &len); + if (val && len >= sizeof(__be32)) + hw->freq = be32_to_cpup(val); + } + if (!hw->freq) + hw->freq = SC18IS602_CLOCK; + break; + } + master->bus_num = client->adapter->nr; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST; + master->setup = sc18is602_setup; + master->transfer_one_message = sc18is602_transfer_one; + master->dev.of_node = np; + + error = spi_register_master(master); + if (error) + goto error_reg; + + return 0; + +error_reg: + spi_master_put(master); + return error; +} + +static int sc18is602_remove(struct i2c_client *client) +{ + struct sc18is602 *hw = i2c_get_clientdata(client); + struct spi_master *master = hw->master; + + spi_unregister_master(master); + + return 0; +} + +static const struct i2c_device_id sc18is602_id[] = { + { "sc18is602", sc18is602 }, + { "sc18is602b", sc18is602b }, + { "sc18is603", sc18is603 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sc18is602_id); + +static struct i2c_driver sc18is602_driver = { + .driver = { + .name = "sc18is602", + }, + .probe = sc18is602_probe, + .remove = sc18is602_remove, + .id_table = sc18is602_id, +}; + +module_i2c_driver(sc18is602_driver); + +MODULE_DESCRIPTION("SC18IC602/603 SPI Master Driver"); +MODULE_AUTHOR("Guenter Roeck"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f6f46de1063c8829713cd9d5b960dd8cb66cde8b Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Wed, 22 Aug 2012 15:49:17 +0200 Subject: spi/pl022: Add chip select handling via GPIO This patch adds the ability for the driver to control the chip select directly. This enables independence from cs_control callbacks. Configurable via platform_data, to be extended as DT in the following patch. Based on the initial patch by Alexandre Pereira da Silva Signed-off-by: Roland Stigge Acked-by: Alexandre Pereira da Silva Reviewed-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 48 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 47c6753fb31b..cf47802f00c9 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -40,6 +40,7 @@ #include #include #include +#include /* * This macro is used to define some register default values. @@ -356,6 +357,8 @@ struct vendor_data { * @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 + * @cur_cs: current chip select (gpio) + * @chipselects: list of chipselects (gpios) */ struct pl022 { struct amba_device *adev; @@ -389,6 +392,8 @@ struct pl022 { char *dummypage; bool dma_running; #endif + int cur_cs; + int *chipselects; }; /** @@ -433,6 +438,14 @@ static void null_cs_control(u32 command) pr_debug("pl022: dummy chip select control, CS=0x%x\n", command); } +static void pl022_cs_control(struct pl022 *pl022, u32 command) +{ + if (gpio_is_valid(pl022->cur_cs)) + gpio_set_value(pl022->cur_cs, command); + else + pl022->cur_chip->cs_control(command); +} + /** * giveback - current spi_message is over, schedule next message and call * callback of this message. Assumes that caller already @@ -479,7 +492,7 @@ static void giveback(struct pl022 *pl022) if (next_msg && next_msg->spi != pl022->cur_msg->spi) next_msg = NULL; if (!next_msg || pl022->cur_msg->state == STATE_ERROR) - pl022->cur_chip->cs_control(SSP_CHIP_DESELECT); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); else pl022->next_msg_cs_active = true; @@ -818,8 +831,7 @@ static void dma_callback(void *data) /* Update total bytes transferred */ msg->actual_length += pl022->cur_transfer->len; if (pl022->cur_transfer->cs_change) - pl022->cur_chip-> - cs_control(SSP_CHIP_DESELECT); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); /* Move to next transfer */ msg->state = next_transfer(pl022); @@ -1252,8 +1264,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id) /* Update total bytes transferred */ msg->actual_length += pl022->cur_transfer->len; if (pl022->cur_transfer->cs_change) - pl022->cur_chip-> - cs_control(SSP_CHIP_DESELECT); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); /* Move to next transfer */ msg->state = next_transfer(pl022); tasklet_schedule(&pl022->pump_transfers); @@ -1338,7 +1349,7 @@ static void pump_transfers(unsigned long data) /* Reselect chip select only if cs_change was requested */ if (previous->cs_change) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); } else { /* STATE_START */ message->state = STATE_RUNNING; @@ -1377,7 +1388,7 @@ static void do_interrupt_dma_transfer(struct pl022 *pl022) /* Enable target chip, if not already active */ if (!pl022->next_msg_cs_active) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); if (set_up_next_transfer(pl022, pl022->cur_transfer)) { /* Error path */ @@ -1429,12 +1440,12 @@ static void do_polling_transfer(struct pl022 *pl022) if (previous->delay_usecs) udelay(previous->delay_usecs); if (previous->cs_change) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); } else { /* STATE_START */ message->state = STATE_RUNNING; if (!pl022->next_msg_cs_active) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); } /* Configuration Changing Per Transfer */ @@ -1466,7 +1477,7 @@ static void do_polling_transfer(struct pl022 *pl022) /* 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); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); /* Move to next transfer */ message->state = next_transfer(pl022); } @@ -1495,6 +1506,7 @@ static int pl022_transfer_one_message(struct spi_master *master, /* Setup the SPI using the per chip configuration */ pl022->cur_chip = spi_get_ctldata(msg->spi); + pl022->cur_cs = pl022->chipselects[msg->spi->chip_select]; restore_state(pl022); flush(pl022); @@ -1840,8 +1852,9 @@ static int pl022_setup(struct spi_device *spi) chip->xfer_type = chip_info->com_mode; if (!chip_info->cs_control) { chip->cs_control = null_cs_control; - dev_warn(&spi->dev, - "chip select function is NULL for this chip\n"); + if (!gpio_is_valid(pl022->chipselects[spi->chip_select])) + dev_warn(&spi->dev, + "invalid chip select\n"); } else chip->cs_control = chip_info->cs_control; @@ -1993,7 +2006,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) struct pl022_ssp_controller *platform_info = adev->dev.platform_data; struct spi_master *master; struct pl022 *pl022 = NULL; /*Data for this driver */ - int status = 0; + int status = 0, i; dev_info(&adev->dev, "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid); @@ -2004,7 +2017,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) } /* Allocate master with space for data */ - master = spi_alloc_master(dev, sizeof(struct pl022)); + master = spi_alloc_master(dev, sizeof(struct pl022) + sizeof(int) * + platform_info->num_chipselect); if (master == NULL) { dev_err(&adev->dev, "probe - cannot alloc SPI master\n"); status = -ENOMEM; @@ -2016,6 +2030,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->master_info = platform_info; pl022->adev = adev; pl022->vendor = id->data; + /* Point chipselects to allocated memory beyond the main struct */ + pl022->chipselects = (int *) pl022 + sizeof(struct pl022); /* * Bus Number Which has been Assigned to this SSP controller @@ -2030,6 +2046,10 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware; master->rt = platform_info->rt; + if (platform_info->num_chipselect && platform_info->chipselects) + for (i = 0; i < platform_info->num_chipselect; i++) + pl022->chipselects[i] = platform_info->chipselects[i]; + /* * Supports mode 0-3, loopback, and active low CS. Transfers are * always MS bit first on the original pl022. -- cgit v1.2.3 From 6d3952a7dfa80919842bbe01ac7f693d40a1eb84 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Wed, 22 Aug 2012 15:49:18 +0200 Subject: spi/pl022: Add devicetree support This patch adds device tree support to the spi-pl022 driver. Based on the initial patch by Alexandre Pereira da Silva Signed-off-by: Roland Stigge Acked-by: Alexandre Pereira da Silva Reviewed-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 79 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index cf47802f00c9..959f2acff2d3 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -41,6 +41,7 @@ #include #include #include +#include /* * This macro is used to define some register default values. @@ -1778,12 +1779,14 @@ static const struct pl022_config_chip pl022_default_chip_info = { static int pl022_setup(struct spi_device *spi) { struct pl022_config_chip const *chip_info; + struct pl022_config_chip chip_info_dt; struct chip_data *chip; struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0}; int status = 0; struct pl022 *pl022 = spi_master_get_devdata(spi->master); unsigned int bits = spi->bits_per_word; u32 tmp; + struct device_node *np = spi->dev.of_node; if (!spi->max_speed_hz) return -EINVAL; @@ -1806,10 +1809,32 @@ 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"); + if (np) { + chip_info_dt = pl022_default_chip_info; + + chip_info_dt.hierarchy = SSP_MASTER; + of_property_read_u32(np, "pl022,interface", + &chip_info_dt.iface); + of_property_read_u32(np, "pl022,com-mode", + &chip_info_dt.com_mode); + of_property_read_u32(np, "pl022,rx-level-trig", + &chip_info_dt.rx_lev_trig); + of_property_read_u32(np, "pl022,tx-level-trig", + &chip_info_dt.tx_lev_trig); + of_property_read_u32(np, "pl022,ctrl-len", + &chip_info_dt.ctrl_len); + of_property_read_u32(np, "pl022,wait-state", + &chip_info_dt.wait_state); + of_property_read_u32(np, "pl022,duplex", + &chip_info_dt.duplex); + + chip_info = &chip_info_dt; + } else { + chip_info = &pl022_default_chip_info; + /* spi_board_info.controller_data not is supplied */ + dev_dbg(&spi->dev, + "using default controller_data settings\n"); + } } else dev_dbg(&spi->dev, "using user supplied controller_data settings\n"); @@ -2006,7 +2031,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) struct pl022_ssp_controller *platform_info = adev->dev.platform_data; struct spi_master *master; struct pl022 *pl022 = NULL; /*Data for this driver */ - int status = 0, i; + struct device_node *np = adev->dev.of_node; + int status = 0, i, num_cs; dev_info(&adev->dev, "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid); @@ -2016,9 +2042,19 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) goto err_no_pdata; } + if (platform_info->num_chipselect) { + num_cs = platform_info->num_chipselect; + } else if (IS_ENABLED(CONFIG_OF)) { + of_property_read_u32(np, "num-cs", &num_cs); + } else { + dev_err(&adev->dev, "probe: no chip select defined\n"); + status = -ENODEV; + goto err_no_pdata; + } + /* Allocate master with space for data */ master = spi_alloc_master(dev, sizeof(struct pl022) + sizeof(int) * - platform_info->num_chipselect); + num_cs); if (master == NULL) { dev_err(&adev->dev, "probe - cannot alloc SPI master\n"); status = -ENOMEM; @@ -2038,17 +2074,41 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) * on this board */ master->bus_num = platform_info->bus_id; - master->num_chipselect = platform_info->num_chipselect; + master->num_chipselect = num_cs; master->cleanup = pl022_cleanup; master->setup = pl022_setup; master->prepare_transfer_hardware = pl022_prepare_transfer_hardware; master->transfer_one_message = pl022_transfer_one_message; master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware; master->rt = platform_info->rt; + master->dev.of_node = dev->of_node; - if (platform_info->num_chipselect && platform_info->chipselects) - for (i = 0; i < platform_info->num_chipselect; i++) + if (platform_info->num_chipselect && platform_info->chipselects) { + for (i = 0; i < num_cs; i++) pl022->chipselects[i] = platform_info->chipselects[i]; + } else if (IS_ENABLED(CONFIG_OF)) { + for (i = 0; i < num_cs; i++) { + int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); + + if (cs_gpio == -EPROBE_DEFER) { + status = -EPROBE_DEFER; + goto err_no_gpio; + } + + pl022->chipselects[i] = cs_gpio; + + if (gpio_is_valid(cs_gpio)) { + if (gpio_request(cs_gpio, "ssp-pl022")) + dev_err(&adev->dev, + "could not request %d gpio\n", + cs_gpio); + else if (gpio_direction_output(cs_gpio, 1)) + dev_err(&adev->dev, + "could set gpio %d as output\n", + cs_gpio); + } + } + } /* * Supports mode 0-3, loopback, and active low CS. Transfers are @@ -2158,6 +2218,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) err_no_ioremap: amba_release_regions(adev); err_no_ioregion: + err_no_gpio: spi_master_put(master); err_no_master: err_no_pdata: -- cgit v1.2.3 From 58ed90de3ef58a19c035355a4a0cd6dfef6d6b0c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 22 Aug 2012 17:28:55 -0700 Subject: spi/sc18is602: Return -EINVAL for probe failures due to I2C function mismatch If the I2C bus master driver does not support the required functionality, the driver returns -ENODEV. This causes a silent probe failure without error message. Since the device has to be explicitly instantiated, and the user should know the correct bus, this event really reflects an error condition. Replace error return value with -EINVAL to trigger an error message showing that the probe function failed. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-sc18is602.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index dd9896423f0b..9eda21d739c6 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -275,7 +275,7 @@ static int sc18is602_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -ENODEV; + return -EINVAL; master = spi_alloc_master(dev, sizeof(struct sc18is602)); if (!master) -- cgit v1.2.3 From c895db0fb2c9e77fd94ba7995e2a01652a91910c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 24 Aug 2012 04:34:18 +0200 Subject: mxs/spi: Restart the block after unsuccessful transfer Restart the SSP block in case the SSP transfer failed in any way. The block hung in some cases otherwise. Signed-off-by: Marek Vasut Cc: Chris Ball Cc: Shawn Guo Cc: Mark Brown Cc: Fabio Estevam Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 130a43688352..447f917fe66f 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -439,8 +439,10 @@ static int mxs_spi_transfer_one(struct spi_master *master, } m->actual_length += t->len; - if (status) + if (status) { + stmp_reset_block(ssp->base); break; + } first = last = 0; } -- cgit v1.2.3 From 41682e03d4fdc947dbd22725d70f222cc7746852 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 24 Aug 2012 04:56:27 +0200 Subject: mxs/spi: Fix misuse of init_completion The init_completion() call does reinit not only the variable carrying the flag that the completion finished, but also initialized the waitqueue associated with the completion. On the contrary, the INIT_COMPLETION() call only reinits the flag. In case there was anything still stuck in the waitqueue, subsequent call to init_completion() would be able to create possible race condition. This patch uses the proper function and moves init_completion() into .probe() call of the driver, to be issued only once. Note that such scenario is impossible, since two threads can never enter the mxs_spi_txrx_dma(), since whole this section is protected by mutex in SPI core. This by no means allows this issue to exit though. Signed-off-by: Marek Vasut Cc: Chris Ball Cc: Shawn Guo Cc: Mark Brown Cc: Fabio Estevam Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 447f917fe66f..4e7801dd571a 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -230,7 +230,7 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, return -EINVAL; } - init_completion(&spi->c); + INIT_COMPLETION(spi->c); if (*first) pio |= BM_SSP_CTRL0_LOCK_CS; @@ -549,6 +549,8 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) ssp->devid = devid; ssp->dma_channel = dma_channel; + init_completion(&spi->c); + ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0, DRIVER_NAME, ssp); if (ret) -- cgit v1.2.3 From 154390dcf826dc97b9d338305db14582c32cb58a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 23 Aug 2012 20:08:47 -0700 Subject: spi/stmp: Fix device remove function The call sequence spi_alloc_master/spi_register_master/spi_unregister_master is complete; it reduces the device reference count to zero, which results in device memory being freed. The remove function accesses the freed memory after the call to spi_unregister_master(), _and_ it calls spi_master_put on the freed memory. Acquire a reference to the SPI master device and release it after cleanup is complete (with the existing spi_master_put) to solve the problem. Also, the device subsystem ensures that the remove function is only called once, and resets device driver data to NULL. Remove the respective check and drop the unnecessaary call to platform_set_drvdata(). Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-stmp.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stmp.c b/drivers/spi/spi-stmp.c index 58e385285323..911e904b3c84 100644 --- a/drivers/spi/spi-stmp.c +++ b/drivers/spi/spi-stmp.c @@ -594,9 +594,7 @@ static int __devexit stmp_spi_remove(struct platform_device *dev) struct stmp_spi *ss; struct spi_master *master; - master = platform_get_drvdata(dev); - if (master == NULL) - goto out0; + master = spi_master_get(platform_get_drvdata(dev)); ss = spi_master_get_devdata(master); spi_unregister_master(master); @@ -609,8 +607,6 @@ static int __devexit stmp_spi_remove(struct platform_device *dev) destroy_workqueue(ss->workqueue); iounmap(ss->regs); spi_master_put(master); - platform_set_drvdata(dev, NULL); -out0: return 0; } -- cgit v1.2.3 From c68025bf0363a7b44be2de512a88a57d57e63d81 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 23 Aug 2012 20:08:48 -0700 Subject: spi/tegra: Fix device remove function The call to spi_unregister_master() in the device remove function frees device memory, and with it any device local data. However, device local data is still accessed after the call to spi_unregister_master(). Acquire a reference to the SPI master device and release it after cleanup is complete to solve the problem. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-tegra.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c index ef52c1c6f5c5..e28445d8d27f 100644 --- a/drivers/spi/spi-tegra.c +++ b/drivers/spi/spi-tegra.c @@ -652,7 +652,7 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) struct spi_tegra_data *tspi; struct resource *r; - master = dev_get_drvdata(&pdev->dev); + master = spi_master_get(dev_get_drvdata(&pdev->dev)); tspi = spi_master_get_devdata(master); spi_unregister_master(master); @@ -668,6 +668,8 @@ 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, resource_size(r)); -- cgit v1.2.3 From 63002e84a80c02b0be0f73932b955755903baabf Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 23 Aug 2012 20:08:49 -0700 Subject: spi/topcliff-pch: Fix device remove function The call sequence spi_alloc_master/spi_register_master/spi_unregister_master is complete; it reduces the device reference count to zero, which results in device memory being freed. An extra call to spi_master_put is unnecessary and results in an access to free memory. Drop it. Also, the device subsystem resets device driver data to NULL after the call to the remove function returns, so there is no need to do it here. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-topcliff-pch.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index cd56dcf46320..159dafd2f908 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1536,8 +1536,6 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev) pci_iounmap(board_dat->pdev, data->io_remap_addr); spi_unregister_master(data->master); - spi_master_put(data->master); - platform_set_drvdata(plat_dev, NULL); return 0; } -- cgit v1.2.3 From 9a2a52452575a4edf5b91e341d7d87fe2be3474c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 16 Aug 2012 20:14:25 -0700 Subject: spi/s3c64xx: Drop extra calls to spi_master_get in suspend/remove functions Suspend and resume functions call spi_master_get() without matching spi_master_put(). The extra references are unnecessary and cause subsequent module unload attempts to fail. Drop the calls. Signed-off-by: Guenter Roeck Acked-by: Kukjin Kim Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index cfa2c35dfeed..0087139090e4 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1409,7 +1409,7 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int s3c64xx_spi_suspend(struct device *dev) { - struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); + struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); spi_master_suspend(master); @@ -1428,7 +1428,7 @@ static int s3c64xx_spi_suspend(struct device *dev) static int s3c64xx_spi_resume(struct device *dev) { - struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); + struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); struct s3c64xx_spi_info *sci = sdd->cntrlr_info; @@ -1452,7 +1452,7 @@ static int s3c64xx_spi_resume(struct device *dev) #ifdef CONFIG_PM_RUNTIME static int s3c64xx_spi_runtime_suspend(struct device *dev) { - struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); + struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); clk_disable(sdd->clk); @@ -1463,7 +1463,7 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev) static int s3c64xx_spi_runtime_resume(struct device *dev) { - struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); + struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); clk_enable(sdd->src_clk); -- cgit v1.2.3 From 7d520d28dd5287d14b5ec6cf4405a1220ca57d42 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 24 Aug 2012 11:03:02 -0700 Subject: spi/mxs: Fix device remove function The call sequence spi_alloc_master/spi_register_master/spi_unregister_master is complete; it reduces the device reference count to zero, which results in device memory being freed. The remove function accesses the freed memory after the call to spi_unregister_master(), _and_ it calls spi_master_put on the freed memory. Acquire a reference to the SPI master device and release it after cleanup is complete (with the existing spi_master_put) to solve the problem. Also, the device subsystem ensures that the remove function is only called once, and resets device driver data to NULL. Remove the unnecessaary calls to platform_set_drvdata(). Signed-off-by: Guenter Roeck Reviewed-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 4e7801dd571a..10d34ebe9ca3 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -586,7 +586,6 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) return 0; out_free_dma: - platform_set_drvdata(pdev, NULL); dma_release_channel(ssp->dmach); clk_disable_unprepare(ssp->clk); out_master_free: @@ -600,14 +599,12 @@ static int __devexit 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); - platform_set_drvdata(pdev, NULL); - dma_release_channel(ssp->dmach); clk_disable_unprepare(ssp->clk); -- cgit v1.2.3 From 161c2dd3ca143f0dca50d72292bf651eabe2aa23 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 5 Sep 2012 11:04:35 +0200 Subject: spi: spi-gpio: store chipselect information in private structure The spi-gpio driver currently assumes the chipselect gpio number is stored in ->controller_data of the device's static board information. In devicetree environments, this information is unavailable and has to be derived from the DT node. This patch moves the gpio storage to the controller's private data so the DT bindings can easily build upon the driver. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 0b56cfc71fab..ff7263ce12c7 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -46,6 +46,7 @@ struct spi_gpio { struct spi_bitbang bitbang; struct spi_gpio_platform_data pdata; struct platform_device *pdev; + int cs_gpios[0]; }; /*----------------------------------------------------------------------*/ @@ -89,15 +90,21 @@ struct spi_gpio { /*----------------------------------------------------------------------*/ -static inline const struct spi_gpio_platform_data * __pure -spi_to_pdata(const struct spi_device *spi) +static inline struct spi_gpio * __pure +spi_to_spi_gpio(const struct spi_device *spi) { const struct spi_bitbang *bang; - const struct spi_gpio *spi_gpio; + struct spi_gpio *spi_gpio; bang = spi_master_get_devdata(spi->master); spi_gpio = container_of(bang, struct spi_gpio, bitbang); - return &spi_gpio->pdata; + return spi_gpio; +} + +static inline struct spi_gpio_platform_data * __pure +spi_to_pdata(const struct spi_device *spi) +{ + return &spi_to_spi_gpio(spi)->pdata; } /* this is #defined to avoid unused-variable warnings when inlining */ @@ -210,7 +217,8 @@ static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi, static void spi_gpio_chipselect(struct spi_device *spi, int is_active) { - unsigned long cs = (unsigned long) spi->controller_data; + struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); + unsigned int cs = spi_gpio->cs_gpios[spi->chip_select]; /* set initial clock polarity */ if (is_active) @@ -224,8 +232,9 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active) static int spi_gpio_setup(struct spi_device *spi) { - unsigned long cs = (unsigned long) spi->controller_data; - int status = 0; + unsigned int cs = (unsigned int) spi->controller_data; + int status = 0; + struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); if (spi->bits_per_word > 32) return -EINVAL; @@ -239,8 +248,11 @@ static int spi_gpio_setup(struct spi_device *spi) !(spi->mode & SPI_CS_HIGH)); } } - if (!status) + if (!status) { status = spi_bitbang_setup(spi); + spi_gpio->cs_gpios[spi->chip_select] = cs; + } + if (status) { if (!spi->controller_state && cs != SPI_GPIO_NO_CHIPSELECT) gpio_free(cs); @@ -250,7 +262,8 @@ static int spi_gpio_setup(struct spi_device *spi) static void spi_gpio_cleanup(struct spi_device *spi) { - unsigned long cs = (unsigned long) spi->controller_data; + struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); + unsigned int cs = spi_gpio->cs_gpios[spi->chip_select]; if (cs != SPI_GPIO_NO_CHIPSELECT) gpio_free(cs); @@ -331,7 +344,8 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev) if (status < 0) return status; - master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio); + master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) + + (sizeof(int) * SPI_N_CHIPSEL)); if (!master) { status = -ENOMEM; goto gpio_free; -- cgit v1.2.3 From 38ab18caa0ad9c844ba60f9618c5de6d6954da3e Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 5 Sep 2012 11:04:36 +0200 Subject: spi: spi-gpio: Add DT bindings This patch adds DT bindings to the spi-gpio driver and some documentation about how to use it. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index ff7263ce12c7..aed161595840 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -232,13 +234,27 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active) static int spi_gpio_setup(struct spi_device *spi) { - unsigned int cs = (unsigned int) spi->controller_data; + unsigned int cs; int status = 0; struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); + struct device_node *np = spi->master->dev.of_node; if (spi->bits_per_word > 32) return -EINVAL; + if (np) { + /* + * In DT environments, the CS GPIOs have already been + * initialized from the "cs-gpios" property of the node. + */ + cs = spi_gpio->cs_gpios[spi->chip_select]; + } else { + /* + * ... otherwise, take it from spi->controller_data + */ + cs = (unsigned int) spi->controller_data; + } + if (!spi->controller_state) { if (cs != SPI_GPIO_NO_CHIPSELECT) { status = gpio_request(cs, dev_name(&spi->dev)); @@ -250,6 +266,7 @@ static int spi_gpio_setup(struct spi_device *spi) } if (!status) { status = spi_bitbang_setup(spi); + /* in case it was initialized from static board data */ spi_gpio->cs_gpios[spi->chip_select] = cs; } @@ -326,6 +343,55 @@ done: return value; } +#ifdef CONFIG_OF +static struct of_device_id spi_gpio_dt_ids[] = { + { .compatible = "spi-gpio" }, + {} +}; +MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids); + +static int spi_gpio_probe_dt(struct platform_device *pdev) +{ + int ret; + u32 tmp; + struct spi_gpio_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id = + of_match_device(spi_gpio_dt_ids, &pdev->dev); + + if (!of_id) + return 0; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->sck = of_get_named_gpio(np, "gpio-sck", 0); + pdata->miso = of_get_named_gpio(np, "gpio-miso", 0); + pdata->mosi = of_get_named_gpio(np, "gpio-mosi", 0); + + ret = of_property_read_u32(np, "num-chipselects", &tmp); + if (ret < 0) { + dev_err(&pdev->dev, "num-chipselects property not found\n"); + goto error_free; + } + + pdata->num_chipselect = tmp; + pdev->dev.platform_data = pdata; + + return 1; + +error_free: + devm_kfree(&pdev->dev, pdata); + return ret; +} +#else +static inline int spi_probe_dt(struct platform_device *) +{ + return 0; +} +#endif + static int __devinit spi_gpio_probe(struct platform_device *pdev) { int status; @@ -333,6 +399,13 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev) struct spi_gpio *spi_gpio; struct spi_gpio_platform_data *pdata; u16 master_flags = 0; + bool use_of = 0; + + status = spi_gpio_probe_dt(pdev); + if (status < 0) + return status; + if (status > 0) + use_of = 1; pdata = pdev->dev.platform_data; #ifdef GENERIC_BITBANG @@ -362,6 +435,23 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev) master->num_chipselect = SPI_N_CHIPSEL; master->setup = spi_gpio_setup; master->cleanup = spi_gpio_cleanup; +#ifdef CONFIG_OF + master->dev.of_node = pdev->dev.of_node; + + if (use_of) { + int i; + struct device_node *np = pdev->dev.of_node; + + /* + * In DT environments, take the CS GPIO from the "cs-gpios" + * property of the node. + */ + + for (i = 0; i < SPI_N_CHIPSEL; i++) + spi_gpio->cs_gpios[i] = + of_get_named_gpio(np, "cs-gpios", i); + } +#endif spi_gpio->bitbang.master = spi_master_get(master); spi_gpio->bitbang.chipselect = spi_gpio_chipselect; @@ -422,8 +512,11 @@ static int __devexit spi_gpio_remove(struct platform_device *pdev) MODULE_ALIAS("platform:" DRIVER_NAME); static struct platform_driver spi_gpio_driver = { - .driver.name = DRIVER_NAME, - .driver.owner = THIS_MODULE, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(spi_gpio_dt_ids), + }, .probe = spi_gpio_probe, .remove = __devexit_p(spi_gpio_remove), }; -- cgit v1.2.3 From 010b481834b2b60f7d8543263a63e69396019f7b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 4 Sep 2012 04:40:15 +0200 Subject: mxs/spi: Fix issues when doing long continuous transfer When doing long continuous transfer, eg. from SPI flash via /dev/mtd, the driver dies. This is caused by a bug in the DMA chaining. Rework the DMA transfer code so that this issue does not happen any longer. This involves proper allocation of correct amount of sg-list members. Also, this means proper creation of DMA descriptors. There is actually an important catch to this, the data transfer descriptors must be interleaved with PIO register write descriptor, otherwise the transfer stalls. This can be done in one descriptor, but due to the limitation of the DMA API, it's not possible. It turns out that in order for the SPI DMA to properly support continuous transfers longer than 65280 bytes, there are some very important parts that were left out from the documentation about about the PIO transfer that is used. Firstly, the XFER_SIZE register is not written with the whole length of a transfer, but is written by each and every chained descriptor with the length of the descriptors data buffer. Next, unlike the demo code supplied by FSL, which only writes one PIO word per descriptor, this does not apply if the descriptors are chained, since the XFER_SIZE register must be written. Therefore, it is essential to use four PIO words, CTRL0, CMD0, CMD1, XFER_SIZE. CMD0 and CMD1 are written with zero, since they don't apply. The DMA programs the PIO words in an incrementing order, so four PIO words. Finally, unlike the demo code supplied by FSL, the SSP_CTRL0_IGNORE_CRC must not be set during the whole transfer, but it must be set only on the last descriptor in the chain. Lastly, this code lends code from drivers/mtd/nand/omap2.c, which solves trouble when the buffer supplied to the DMA transfer was vmalloc()'d. So with this patch, it's safe to use /dev/mtdblockX interface again. Signed-off-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 141 +++++++++++++++++++++++++++++++------------------- 1 file changed, 88 insertions(+), 53 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 10d34ebe9ca3..bcba098e97c5 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -53,9 +53,9 @@ #define DRIVER_NAME "mxs-spi" -#define SSP_TIMEOUT 1000 /* 1000 ms */ +/* Use 10S timeout for very long transfers, it should suffice. */ +#define SSP_TIMEOUT 10000 -#define SG_NUM 4 #define SG_MAXLEN 0xff00 struct mxs_spi { @@ -219,61 +219,94 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, int *first, int *last, int write) { struct mxs_ssp *ssp = &spi->ssp; - struct dma_async_tx_descriptor *desc; - struct scatterlist sg[SG_NUM]; + struct dma_async_tx_descriptor *desc = NULL; + const bool vmalloced_buf = is_vmalloc_addr(buf); + const int desc_len = vmalloced_buf ? PAGE_SIZE : SG_MAXLEN; + const int sgs = DIV_ROUND_UP(len, desc_len); int sg_count; - uint32_t pio = BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); - int ret; - - if (len > SG_NUM * SG_MAXLEN) { - dev_err(ssp->dev, "Data chunk too big for DMA\n"); + int min, ret; + uint32_t ctrl0; + struct page *vm_page; + void *sg_buf; + struct { + uint32_t pio[4]; + struct scatterlist sg; + } *dma_xfer; + + if (!len) return -EINVAL; - } + + dma_xfer = kzalloc(sizeof(*dma_xfer) * sgs, GFP_KERNEL); + if (!dma_xfer) + return -ENOMEM; INIT_COMPLETION(spi->c); + ctrl0 = readl(ssp->base + HW_SSP_CTRL0); + ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); + if (*first) - pio |= BM_SSP_CTRL0_LOCK_CS; - if (*last) - pio |= BM_SSP_CTRL0_IGNORE_CRC; + ctrl0 |= BM_SSP_CTRL0_LOCK_CS; if (!write) - pio |= BM_SSP_CTRL0_READ; - - if (ssp->devid == IMX23_SSP) - pio |= len; - else - writel(len, ssp->base + HW_SSP_XFER_SIZE); - - /* Queue the PIO register write transfer. */ - desc = dmaengine_prep_slave_sg(ssp->dmach, - (struct scatterlist *)&pio, - 1, DMA_TRANS_NONE, 0); - if (!desc) { - dev_err(ssp->dev, - "Failed to get PIO reg. write descriptor.\n"); - return -EINVAL; - } + ctrl0 |= BM_SSP_CTRL0_READ; /* Queue the DMA data transfer. */ - sg_init_table(sg, (len / SG_MAXLEN) + 1); - sg_count = 0; - while (len) { - sg_set_buf(&sg[sg_count++], buf, min(len, SG_MAXLEN)); - len -= min(len, SG_MAXLEN); - buf += min(len, SG_MAXLEN); - } - dma_map_sg(ssp->dev, sg, sg_count, - write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - desc = dmaengine_prep_slave_sg(ssp->dmach, sg, sg_count, - write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - - if (!desc) { - dev_err(ssp->dev, - "Failed to get DMA data write descriptor.\n"); - ret = -EINVAL; - goto err; + for (sg_count = 0; sg_count < sgs; sg_count++) { + min = min(len, desc_len); + + /* Prepare the transfer descriptor. */ + if ((sg_count + 1 == sgs) && *last) + ctrl0 |= BM_SSP_CTRL0_IGNORE_CRC; + + if (ssp->devid == IMX23_SSP) + ctrl0 |= min; + + dma_xfer[sg_count].pio[0] = ctrl0; + dma_xfer[sg_count].pio[3] = min; + + if (vmalloced_buf) { + vm_page = vmalloc_to_page(buf); + if (!vm_page) { + ret = -ENOMEM; + goto err_vmalloc; + } + sg_buf = page_address(vm_page) + + ((size_t)buf & ~PAGE_MASK); + } else { + sg_buf = buf; + } + + sg_init_one(&dma_xfer[sg_count].sg, sg_buf, min); + ret = dma_map_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, + write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + len -= min; + buf += min; + + /* Queue the PIO register write transfer. */ + desc = dmaengine_prep_slave_sg(ssp->dmach, + (struct scatterlist *)dma_xfer[sg_count].pio, + (ssp->devid == IMX23_SSP) ? 1 : 4, + DMA_TRANS_NONE, + sg_count ? DMA_PREP_INTERRUPT : 0); + if (!desc) { + dev_err(ssp->dev, + "Failed to get PIO reg. write descriptor.\n"); + ret = -EINVAL; + goto err_mapped; + } + + desc = dmaengine_prep_slave_sg(ssp->dmach, + &dma_xfer[sg_count].sg, 1, + write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (!desc) { + dev_err(ssp->dev, + "Failed to get DMA data write descriptor.\n"); + ret = -EINVAL; + goto err_mapped; + } } /* @@ -289,21 +322,23 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, ret = wait_for_completion_timeout(&spi->c, msecs_to_jiffies(SSP_TIMEOUT)); - if (!ret) { dev_err(ssp->dev, "DMA transfer timeout\n"); ret = -ETIMEDOUT; - goto err; + goto err_vmalloc; } ret = 0; -err: - for (--sg_count; sg_count >= 0; sg_count--) { - dma_unmap_sg(ssp->dev, &sg[sg_count], 1, +err_vmalloc: + while (--sg_count >= 0) { +err_mapped: + dma_unmap_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); } + kfree(dma_xfer); + return ret; } -- cgit v1.2.3 From 204e706fa2ac1a62ff6423039945eb567c6c7efc Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 4 Sep 2012 04:40:16 +0200 Subject: mxs/spi: Increment the transfer length only if transfer succeeded The transfer function incremented (struct spi_message)->actual_length unconditionally, even if the transfer failed. Rectify this by incrementing this only if transfer succeeded. Signed-off-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index bcba098e97c5..138c8523dbba 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -473,12 +473,12 @@ static int mxs_spi_transfer_one(struct spi_master *master, &first, &last, 0); } - m->actual_length += t->len; if (status) { stmp_reset_block(ssp->base); break; } + m->actual_length += t->len; first = last = 0; } -- cgit v1.2.3 From 727c10e3e54c4404f6842d246b15fe3703d33556 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 4 Sep 2012 04:40:17 +0200 Subject: mxs/spi: Decrement the DMA/PIO border This driver checks the length of transfer to be made and based on this information, either chooses to transfer data via DMA or PIO. Decrement this border further to gain better performace eg. during SPI flash writes. Empiric measurement shows that this gives extra 3kB/s write speed with a M25P80 flash clocked at 40MHz. Signed-off-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 138c8523dbba..21e1dcad3914 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -445,7 +445,7 @@ static int mxs_spi_transfer_one(struct spi_master *master, * DMA only: 2.164808 seconds, 473.0KB/s * Combined: 1.676276 seconds, 610.9KB/s */ - if (t->len <= 256) { + if (t->len < 32) { writel(BM_SSP_CTRL1_DMA_ENABLE, ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR); -- cgit v1.2.3 From f13639dc6043eb67e308aa5cf96717a86c10f8b9 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 4 Sep 2012 04:40:18 +0200 Subject: mxs/spi: Rework the mxs_ssp_timeout to be more readable Rework the mxs_ssp_timeout() function to make it a bit more readable and hopefully less error prone. Also, have only one successful exit from the function and one failing exit instead of two. Finally, discard the udelay() from this function altogether, as this tightloop is quick enough it's pointless. Signed-off-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 21e1dcad3914..556e5ef907fa 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -177,25 +177,23 @@ static inline void mxs_spi_disable(struct mxs_spi *spi) static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set) { - unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); + const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); struct mxs_ssp *ssp = &spi->ssp; uint32_t reg; - while (1) { + do { reg = readl_relaxed(ssp->base + offset); - if (set && ((reg & mask) == mask)) - break; + if (!set) + reg = ~reg; - if (!set && ((~reg & mask) == mask)) - break; + reg &= mask; - udelay(1); + if (reg == mask) + return 0; + } while (time_before(jiffies, timeout)); - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - } - return 0; + return -ETIMEDOUT; } static void mxs_ssp_dma_irq_callback(void *param) -- cgit v1.2.3 From 38e271cde5ae62b0b94ff8e8e7e1e1395da7dbf5 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 4 Sep 2012 16:58:35 +0530 Subject: spi: spi-tle62x0: Use module_spi_driver macro module_spi_driver eliminates module_init and module_exit calls and makes the code simpler. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- drivers/spi/spi-tle62x0.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tle62x0.c b/drivers/spi/spi-tle62x0.c index 0ce5c12aab55..24421024deaf 100644 --- a/drivers/spi/spi-tle62x0.c +++ b/drivers/spi/spi-tle62x0.c @@ -316,18 +316,7 @@ static struct spi_driver tle62x0_driver = { .remove = __devexit_p(tle62x0_remove), }; -static __init int tle62x0_init(void) -{ - return spi_register_driver(&tle62x0_driver); -} - -static __exit void tle62x0_exit(void) -{ - spi_unregister_driver(&tle62x0_driver); -} - -module_init(tle62x0_init); -module_exit(tle62x0_exit); +module_spi_driver(tle62x0_driver); MODULE_AUTHOR("Ben Dooks "); MODULE_DESCRIPTION("TLE62x0 SPI driver"); -- cgit v1.2.3 From b4b8482690d97ea5421acf71e9e397fe0c5a25b8 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Mon, 3 Sep 2012 10:14:29 +0200 Subject: spi/pl022: Fix chipselects pointer computation The new chip select handling via GPIO introduced a pointer computation bug: (int *) pl022 + sizeof(struct pl022) doesn't point to the data immediately after the actual struct pl022 (as was intended) but to a multiple of bytes after it because of the (int *) type. Replacing the kludgy pointer arithmetic with managed memory allocation for the chip selects. Reported-by: Shiraz Hashim Signed-off-by: Roland Stigge Reviewed-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 959f2acff2d3..827ad5152d8b 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2053,8 +2053,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) } /* Allocate master with space for data */ - master = spi_alloc_master(dev, sizeof(struct pl022) + sizeof(int) * - num_cs); + master = spi_alloc_master(dev, sizeof(struct pl022)); if (master == NULL) { dev_err(&adev->dev, "probe - cannot alloc SPI master\n"); status = -ENOMEM; @@ -2066,8 +2065,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->master_info = platform_info; pl022->adev = adev; pl022->vendor = id->data; - /* Point chipselects to allocated memory beyond the main struct */ - pl022->chipselects = (int *) pl022 + sizeof(struct pl022); + pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int), + GFP_KERNEL); /* * Bus Number Which has been Assigned to this SSP controller -- cgit v1.2.3 From 78bfee0e1e2e22c3062b41be8db618e7484c8e35 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 1 Sep 2012 18:33:08 +0200 Subject: spi: spi-sh-hspi: drop frees of devm_ alloc'd data devm free functions should not have to be explicitly used. A semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ @@ ( * devm_kfree(...); | * devm_free_irq(...); | * devm_iounmap(...); | * devm_release_region(...); | * devm_release_mem_region(...); ) // Signed-off-by: Julia Lawall Signed-off-by: Mark Brown --- drivers/spi/spi-sh-hspi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 934138c7b3d3..796c077ef439 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -283,7 +283,7 @@ static int __devinit hspi_probe(struct platform_device *pdev) ret = spi_register_master(master); if (ret < 0) { dev_err(&pdev->dev, "spi_register_master error.\n"); - goto error2; + goto error1; } pm_runtime_enable(&pdev->dev); @@ -292,8 +292,6 @@ static int __devinit hspi_probe(struct platform_device *pdev) return 0; - error2: - devm_iounmap(hspi->dev, hspi->addr); error1: clk_put(clk); error0: @@ -310,7 +308,6 @@ static int __devexit hspi_remove(struct platform_device *pdev) clk_put(hspi->clk); spi_unregister_master(hspi->master); - devm_iounmap(hspi->dev, hspi->addr); return 0; } -- cgit v1.2.3 From e64d07a2dae569fc3c938adac777562a1d6f151e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 22 Aug 2012 22:38:35 +0200 Subject: spi/mxs: Make the SPI block clock speed configurable via DT Add "clock-frequency" property, which allows configuring the SPI block's base speed. Signed-off-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 556e5ef907fa..edf1360ab09e 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -520,10 +520,17 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) struct pinctrl *pinctrl; struct clk *clk; void __iomem *base; - int devid, dma_channel; + int devid, dma_channel, clk_freq; int ret = 0, irq_err, irq_dma; dma_cap_mask_t mask; + /* + * Default clock speed for the SPI core. 160MHz seems to + * work reasonably well with most SPI flashes, so use this + * as a default. Override with "clock-frequency" DT prop. + */ + const int clk_freq_default = 160000000; + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq_err = platform_get_irq(pdev, 0); irq_dma = platform_get_irq(pdev, 1); @@ -555,12 +562,18 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) "Failed to get DMA channel\n"); return -EINVAL; } + + ret = of_property_read_u32(np, "clock-frequency", + &clk_freq); + if (ret) + clk_freq = clk_freq_default; } else { dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!dmares) return -EINVAL; devid = pdev->id_entry->driver_data; dma_channel = dmares->start; + clk_freq = clk_freq_default; } master = spi_alloc_master(&pdev->dev, sizeof(*spi)); @@ -598,12 +611,8 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) goto out_master_free; } - /* - * Crank up the clock to 120MHz, this will be further divided onto a - * proper speed. - */ clk_prepare_enable(ssp->clk); - clk_set_rate(ssp->clk, 120 * 1000 * 1000); + clk_set_rate(ssp->clk, clk_freq); ssp->clk_rate = clk_get_rate(ssp->clk) / 1000; stmp_reset_block(ssp->base); -- cgit v1.2.3 From ac2cb30b4792340d932545f41a8335da2632027d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 7 Sep 2012 08:23:06 +0800 Subject: spi/gpio: Fix stub for spi_gpio_probe_dt() The gpio_ was missing from the name. Add a name for the parameter while we're at it since GCC warns. Reported-by: Stephen Rothwell Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index aed161595840..a2b50c516b31 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -386,7 +386,7 @@ error_free: return ret; } #else -static inline int spi_probe_dt(struct platform_device *) +static inline int spi_gpio_probe_dt(struct platform_device *pdev) { return 0; } -- cgit v1.2.3 From d7b4394e780b02511c8a7a499380cdd56316c770 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Tue, 11 Sep 2012 12:13:20 +0530 Subject: spi: omap2-mcspi: Cleanup the omap2_mcspi_txrx_dma function Currently in omap2_mcspi_txrx_dma the tx and the rx support is interleaved. Make the rx related code in omap2_mcspi_rx_dma and the tx related code omap2_mcspi_tx_dma and call the functions. While at it remove the braces in the if statements which has only one line. Also fix ["foo * bar" to "foo *bar"] warn for the rx and tx variables. Only a cleanup no functional change. Signed-off-by: Shubhrajyoti D Tested-by: Felipe Balbi Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 256 ++++++++++++++++++++++++------------------ 1 file changed, 144 insertions(+), 112 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 164c15d6a1bb..5560b708e628 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -315,49 +315,27 @@ static void omap2_mcspi_tx_callback(void *data) omap2_mcspi_set_dma_req(spi, 0, 0); } -static unsigned -omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) +static void omap2_mcspi_tx_dma(struct spi_device *spi, + struct spi_transfer *xfer, + struct dma_slave_config cfg) { struct omap2_mcspi *mcspi; - struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi_dma *mcspi_dma; unsigned int count; - int word_len, element_count; - int elements = 0; - u32 l; u8 * rx; const u8 * tx; void __iomem *chstat_reg; - struct dma_slave_config cfg; - enum dma_slave_buswidth width; - unsigned es; + struct omap2_mcspi_cs *cs = spi->controller_state; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - l = mcspi_cached_chconf0(spi); + count = xfer->len; + rx = xfer->rx_buf; + tx = xfer->tx_buf; chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; - if (cs->word_len <= 8) { - width = DMA_SLAVE_BUSWIDTH_1_BYTE; - es = 1; - } else if (cs->word_len <= 16) { - width = DMA_SLAVE_BUSWIDTH_2_BYTES; - es = 2; - } else { - width = DMA_SLAVE_BUSWIDTH_4_BYTES; - es = 4; - } - - memset(&cfg, 0, sizeof(cfg)); - cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0; - cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0; - cfg.src_addr_width = width; - cfg.dst_addr_width = width; - cfg.src_maxburst = 1; - cfg.dst_maxburst = 1; - - if (xfer->tx_buf && mcspi_dma->dma_tx) { + if (mcspi_dma->dma_tx) { struct dma_async_tx_descriptor *tx; struct scatterlist sg; @@ -368,7 +346,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) sg_dma_len(&sg) = xfer->len; tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1, - DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { tx->callback = omap2_mcspi_tx_callback; tx->callback_param = spi; @@ -377,8 +355,50 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) /* FIXME: fall back to PIO? */ } } + dma_async_issue_pending(mcspi_dma->dma_tx); + omap2_mcspi_set_dma_req(spi, 0, 1); - if (xfer->rx_buf && mcspi_dma->dma_rx) { + wait_for_completion(&mcspi_dma->dma_tx_completion); + dma_unmap_single(mcspi->dev, 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"); + } +} + +static unsigned +omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, + struct dma_slave_config cfg, + unsigned es) +{ + struct omap2_mcspi *mcspi; + struct omap2_mcspi_dma *mcspi_dma; + unsigned int count; + u32 l; + int elements = 0; + int word_len, element_count; + struct omap2_mcspi_cs *cs = spi->controller_state; + mcspi = spi_master_get_devdata(spi->master); + mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + count = xfer->len; + word_len = cs->word_len; + l = mcspi_cached_chconf0(spi); + + if (word_len <= 8) + element_count = count; + else if (word_len <= 16) + element_count = count >> 1; + else /* word_len <= 32 */ + element_count = count >> 2; + + if (mcspi_dma->dma_rx) { struct dma_async_tx_descriptor *tx; struct scatterlist sg; size_t len = xfer->len - es; @@ -393,108 +413,120 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) sg_dma_len(&sg) = len; tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); if (tx) { tx->callback = omap2_mcspi_rx_callback; tx->callback_param = spi; dmaengine_submit(tx); } else { - /* FIXME: fall back to PIO? */ - } - } - - count = xfer->len; - word_len = cs->word_len; - - rx = xfer->rx_buf; - tx = xfer->tx_buf; - - if (word_len <= 8) { - element_count = count; - } else if (word_len <= 16) { - element_count = count >> 1; - } else /* word_len <= 32 */ { - element_count = count >> 2; - } - - if (tx != NULL) { - dma_async_issue_pending(mcspi_dma->dma_tx); - omap2_mcspi_set_dma_req(spi, 0, 1); - } - - if (rx != NULL) { - dma_async_issue_pending(mcspi_dma->dma_rx); - omap2_mcspi_set_dma_req(spi, 1, 1); - } - - if (tx != NULL) { - wait_for_completion(&mcspi_dma->dma_tx_completion); - dma_unmap_single(mcspi->dev, 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"); + /* FIXME: fall back to PIO? */ } } - if (rx != NULL) { - wait_for_completion(&mcspi_dma->dma_rx_completion); - dma_unmap_single(mcspi->dev, xfer->rx_dma, count, - DMA_FROM_DEVICE); - omap2_mcspi_set_enable(spi, 0); + dma_async_issue_pending(mcspi_dma->dma_rx); + omap2_mcspi_set_dma_req(spi, 1, 1); - elements = element_count - 1; + wait_for_completion(&mcspi_dma->dma_rx_completion); + dma_unmap_single(mcspi->dev, xfer->rx_dma, count, + DMA_FROM_DEVICE); + omap2_mcspi_set_enable(spi, 0); - if (l & OMAP2_MCSPI_CHCONF_TURBO) { - elements--; + elements = element_count - 1; - if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) - & OMAP2_MCSPI_CHSTAT_RXS)) { - u32 w; - - w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); - if (word_len <= 8) - ((u8 *)xfer->rx_buf)[elements++] = w; - else if (word_len <= 16) - ((u16 *)xfer->rx_buf)[elements++] = w; - else /* word_len <= 32 */ - ((u32 *)xfer->rx_buf)[elements++] = w; - } else { - dev_err(&spi->dev, - "DMA RX penultimate word empty"); - count -= (word_len <= 8) ? 2 : - (word_len <= 16) ? 4 : - /* word_len <= 32 */ 8; - omap2_mcspi_set_enable(spi, 1); - return count; - } - } + if (l & OMAP2_MCSPI_CHCONF_TURBO) { + elements--; if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) - & OMAP2_MCSPI_CHSTAT_RXS)) { + & OMAP2_MCSPI_CHSTAT_RXS)) { u32 w; w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); if (word_len <= 8) - ((u8 *)xfer->rx_buf)[elements] = w; + ((u8 *)xfer->rx_buf)[elements++] = w; else if (word_len <= 16) - ((u16 *)xfer->rx_buf)[elements] = w; + ((u16 *)xfer->rx_buf)[elements++] = w; else /* word_len <= 32 */ - ((u32 *)xfer->rx_buf)[elements] = w; + ((u32 *)xfer->rx_buf)[elements++] = w; } else { - dev_err(&spi->dev, "DMA RX last word empty"); - count -= (word_len <= 8) ? 1 : - (word_len <= 16) ? 2 : - /* word_len <= 32 */ 4; + dev_err(&spi->dev, "DMA RX penultimate word empty"); + count -= (word_len <= 8) ? 2 : + (word_len <= 16) ? 4 : + /* word_len <= 32 */ 8; + omap2_mcspi_set_enable(spi, 1); + return count; } - omap2_mcspi_set_enable(spi, 1); } + if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) + & OMAP2_MCSPI_CHSTAT_RXS)) { + u32 w; + + w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); + if (word_len <= 8) + ((u8 *)xfer->rx_buf)[elements] = w; + else if (word_len <= 16) + ((u16 *)xfer->rx_buf)[elements] = w; + else /* word_len <= 32 */ + ((u32 *)xfer->rx_buf)[elements] = w; + } else { + dev_err(&spi->dev, "DMA RX last word empty"); + count -= (word_len <= 8) ? 1 : + (word_len <= 16) ? 2 : + /* word_len <= 32 */ 4; + } + omap2_mcspi_set_enable(spi, 1); + return count; +} + +static unsigned +omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) +{ + struct omap2_mcspi *mcspi; + struct omap2_mcspi_cs *cs = spi->controller_state; + struct omap2_mcspi_dma *mcspi_dma; + unsigned int count; + u32 l; + u8 *rx; + const u8 *tx; + struct dma_slave_config cfg; + enum dma_slave_buswidth width; + unsigned es; + + mcspi = spi_master_get_devdata(spi->master); + mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + l = mcspi_cached_chconf0(spi); + + + if (cs->word_len <= 8) { + width = DMA_SLAVE_BUSWIDTH_1_BYTE; + es = 1; + } else if (cs->word_len <= 16) { + width = DMA_SLAVE_BUSWIDTH_2_BYTES; + es = 2; + } else { + width = DMA_SLAVE_BUSWIDTH_4_BYTES; + es = 4; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0; + cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0; + cfg.src_addr_width = width; + cfg.dst_addr_width = width; + cfg.src_maxburst = 1; + cfg.dst_maxburst = 1; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + + count = xfer->len; + + if (tx != NULL) + omap2_mcspi_tx_dma(spi, xfer, cfg); + + if (rx != NULL) + return omap2_mcspi_rx_dma(spi, xfer, cfg, es); + return count; } -- cgit v1.2.3 From 4f5e1b370845a0a5789ff7271004ca49e5baa46f Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 19 Sep 2012 14:23:46 +0200 Subject: spi/pl022: adopt pinctrl support Amend the PL022 pin controller to optionally take a pin control handle and set the state of the pins to "default" on boot and runtime resume, and to "sleep" at runtime suspend. This way we will dynamically save power on the SPI busses, for example some electronic designs may be able to ground the pins when unused instead of pull-up. Some pin controllers may want to set the pins as wake-up sources when sleeping. Effect on platforms using the PL022 driver: - If the platform does not use pin control - no semantic effect, the pinctrl stubs will kick in and resolve the situation. - Platforms using this driver and have pin control but no function defined for the PL022 need to either supply a "default" function in their map or enable pinctrl dummies so the driver is satisfied. - Platforms using this driver with hogs for setting up the PL022 pin control - stop using hogs to take the pl022 pin control handle, let the driver handle this. I'be looked at some platforms that may be affected: - SPEAr: appears to define the proper functions in their device trees and not hogging them, so things should be smooth, the driver will simply start to take its pins. - Ux500: the proper function is defined and will be taken properly by the driver. New sleep states introduced by a separate patch to ux500 but no regression, since the default state is sufficient. - U300: old hog deleted as part of this patch. - LPC32xx: does not appear to be using pinctrl. - ARM Integrator IMPD1, RealView & Versatile: does not use pinctrl. Tested-by: Roland Stigge Signed-off-by: Patrice Chotard Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 827ad5152d8b..b6cfb7b3a599 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -42,6 +42,7 @@ #include #include #include +#include /* * This macro is used to define some register default values. @@ -367,6 +368,10 @@ struct pl022 { resource_size_t phybase; void __iomem *virtbase; struct clk *clk; + /* Two optional pin states - default & sleep */ + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; struct spi_master *master; struct pl022_ssp_controller *master_info; /* Message per-transfer pump */ @@ -2068,6 +2073,28 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int), GFP_KERNEL); + pl022->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pl022->pinctrl)) { + status = PTR_ERR(pl022->pinctrl); + goto err_no_pinctrl; + } + + pl022->pins_default = pinctrl_lookup_state(pl022->pinctrl, + PINCTRL_STATE_DEFAULT); + /* enable pins to be muxed in and configured */ + if (!IS_ERR(pl022->pins_default)) { + status = pinctrl_select_state(pl022->pinctrl, + pl022->pins_default); + if (status) + dev_err(dev, "could not set default pins\n"); + } else + dev_err(dev, "could not get default pinstate\n"); + + pl022->pins_sleep = pinctrl_lookup_state(pl022->pinctrl, + PINCTRL_STATE_SLEEP); + if (IS_ERR(pl022->pins_sleep)) + dev_dbg(dev, "could not get sleep pinstate\n"); + /* * Bus Number Which has been Assigned to this SSP controller * on this board @@ -2218,6 +2245,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) amba_release_regions(adev); err_no_ioregion: err_no_gpio: + err_no_pinctrl: spi_master_put(master); err_no_master: err_no_pdata: @@ -2291,15 +2319,33 @@ static int pl022_resume(struct device *dev) static int pl022_runtime_suspend(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); + int status = 0; clk_disable(pl022->clk); + /* Optionally let pins go into sleep states */ + if (!IS_ERR(pl022->pins_sleep)) { + status = pinctrl_select_state(pl022->pinctrl, + pl022->pins_sleep); + if (status) + dev_err(dev, "could not set pins to sleep state\n"); + } + return 0; } static int pl022_runtime_resume(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); + int status = 0; + + /* Optionaly enable pins to be muxed in and configured */ + if (!IS_ERR(pl022->pins_default)) { + status = pinctrl_select_state(pl022->pinctrl, + pl022->pins_default); + if (status) + dev_err(dev, "could not set default pins\n"); + } clk_enable(pl022->clk); -- cgit v1.2.3 From ec155afa82cb10d97b6c10d6ede5b0ffa321e816 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 18 Sep 2012 08:01:25 -0400 Subject: spi: omap2-mcspi: add pinctrl support Adds pinctrl support to support OMAP platforms that boot from DT and rely on pinctrl support to set pinmuxes. Signed-off-by: Matt Porter Acked-by: Shubhrajyoti D Acked-by: Tony Lindgren Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 5560b708e628..9926f2784bba 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include @@ -1148,6 +1150,7 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) static int bus_num = 1; struct device_node *node = pdev->dev.of_node; const struct of_device_id *match; + struct pinctrl *pinctrl; master = spi_alloc_master(&pdev->dev, sizeof *mcspi); if (master == NULL) { @@ -1243,6 +1246,11 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) if (status < 0) goto dma_chnl_free; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + dev_warn(&pdev->dev, + "pins are not configured from the driver\n"); + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); pm_runtime_enable(&pdev->dev); -- cgit v1.2.3 From 5bee3b94d945851d6cb624a5e6808d9a815ebca0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 13 Sep 2012 16:31:30 +0200 Subject: spi/s3c64xx: Don't free controller_data on non-dt platforms When s3c64xx-spi is instantiated from device tree an instance of struct s3c64xx_spi_csinfo is dynamically allocated in the driver. For non-dt platform it is passed from board code through spi_register_board_info(). On error path in s3c64xx_spi_setup() function there is an attempt to free this data struct s3c64xx_spi_csinfo object as it would have been allocated in the driver for both, dt and non-dt based platforms. This leads to following bug when gpio request fails: spi spi1.0: Failed to get /CS gpio [21]: -16 kernel BUG at mm/slub.c:3478! Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM Modules linked in: CPU: 0 Not tainted (3.6.0-rc5-00092-g9b0b493-dirty #6111) PC is at kfree+0x148/0x158 LR is at s3c64xx_spi_setup+0xac/0x290 pc : [] lr : [] psr: 40000013 sp : ee043e10 ip : c032883c fp : c0481f7c r10: ee0abd80 r9 : 00000063 r8 : 00000000 r7 : ee129e78 r6 : ee104a00 r5 : fffffff0 r4 : c047bc64 r3 : 40000400 r2 : c047bc64 r1 : c04def60 r0 : 0004047b Flags: nZcv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 10c5387d Table: 4000404a DAC: 00000015 Process swapper/0 (pid: 1, stack limit = 0xee0422f0) Stack: (0xee043e10 to 0xee044000) ... [] (kfree+0x148/0x158) from [] (s3c64xx_spi_setup+0xac/0x290) [] (s3c64xx_spi_setup+0xac/0x290) from [] (spi_setup+0x34/0x4c) [] (spi_setup+0x34/0x4c) from [] (spi_add_device+0x98/0x128) [] (spi_add_device+0x98/0x128) from [] (spi_new_device+0x74/0xa8) [] (spi_new_device+0x74/0xa8) from [] (spi_match_master_to_boardinfo+0x24/0x44) [] (spi_match_master_to_boardinfo+0x24/0x44) from [] (spi_register_master+0xf4/0x2a8) [] (spi_register_master+0xf4/0x2a8) from [] (s3c64xx_spi_probe+0x34c/0x42c) [] (s3c64xx_spi_probe+0x34c/0x42c) from [] (platform_drv_probe+0x18/0x1c) There should be no attempt to kfree controller_data when it was externally provided through the board code. Fix this by freeing controller_data only when dev->of_node is not null. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Kukjin Kim Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 0087139090e4..c6ad4e1c3dc0 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -976,7 +976,8 @@ err_msgq: spi_set_ctldata(spi, NULL); err_gpio_req: - kfree(cs); + if (spi->dev.of_node) + kfree(cs); return err; } -- cgit v1.2.3 From 39a6ac11df6579df0361922f05c43f0fac8daa37 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Tue, 18 Sep 2012 15:53:53 +0200 Subject: spi/pl022: Devicetree support w/o platform data Even with devicetree support, we needed platform data to provide some data, leading to mixed device tree and platform data. This patch makes it possible to provide all that information via device tree. Now, the data must be provided via platform data _or_ device tree completely. Only in case of DMA where a callback specification is necessary (dma_filter()), platform data is the only option. Signed-off-by: Roland Stigge Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index b6cfb7b3a599..3f2f36c79ab8 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2029,6 +2029,34 @@ static void pl022_cleanup(struct spi_device *spi) kfree(chip); } +static struct pl022_ssp_controller * +pl022_platform_data_dt_get(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct pl022_ssp_controller *pd; + u32 tmp; + + if (!np) { + dev_err(dev, "no dt node defined\n"); + return NULL; + } + + pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL); + if (!pd) { + dev_err(dev, "cannot allocate platform data memory\n"); + return NULL; + } + + pd->bus_id = -1; + of_property_read_u32(np, "num-cs", &tmp); + pd->num_chipselect = tmp; + of_property_read_u32(np, "pl022,autosuspend-delay", + &pd->autosuspend_delay); + pd->rt = of_property_read_bool(np, "pl022,rt"); + + return pd; +} + static int __devinit pl022_probe(struct amba_device *adev, const struct amba_id *id) { @@ -2041,18 +2069,19 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) dev_info(&adev->dev, "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid); - if (platform_info == NULL) { - dev_err(&adev->dev, "probe - no platform data supplied\n"); + if (!platform_info && IS_ENABLED(CONFIG_OF)) + platform_info = pl022_platform_data_dt_get(dev); + + if (!platform_info) { + dev_err(dev, "probe: no platform data defined\n"); status = -ENODEV; goto err_no_pdata; } if (platform_info->num_chipselect) { num_cs = platform_info->num_chipselect; - } else if (IS_ENABLED(CONFIG_OF)) { - of_property_read_u32(np, "num-cs", &num_cs); } else { - dev_err(&adev->dev, "probe: no chip select defined\n"); + dev_err(dev, "probe: no chip select defined\n"); status = -ENODEV; goto err_no_pdata; } -- cgit v1.2.3 From aeef9915b9a40d257ec94f9900a3fb4a15da2ab7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 26 Sep 2012 16:48:36 +0200 Subject: spi/pl022: use more managed resources This switches the PL022 SPI driver to use devm_* managed resources for IRQ, clocks, ioremap and GPIO. Prior to this, the GPIOs would even leak. Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index f8568b43660d..15737bcee9fb 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1,7 +1,7 @@ /* * A driver for the ARM PL022 PrimeCell SSP/SPI bus master. * - * Copyright (C) 2008-2009 ST-Ericsson AB + * Copyright (C) 2008-2012 ST-Ericsson AB * Copyright (C) 2006 STMicroelectronics Pvt. Ltd. * * Author: Linus Walleij @@ -2074,24 +2074,21 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) if (!platform_info) { dev_err(dev, "probe: no platform data defined\n"); - status = -ENODEV; - goto err_no_pdata; + return -ENODEV; } if (platform_info->num_chipselect) { num_cs = platform_info->num_chipselect; } else { dev_err(dev, "probe: no chip select defined\n"); - status = -ENODEV; - goto err_no_pdata; + return -ENODEV; } /* Allocate master with space for data */ master = spi_alloc_master(dev, sizeof(struct pl022)); if (master == NULL) { dev_err(&adev->dev, "probe - cannot alloc SPI master\n"); - status = -ENOMEM; - goto err_no_master; + return -ENOMEM; } pl022 = spi_master_get_devdata(master); @@ -2153,7 +2150,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->chipselects[i] = cs_gpio; if (gpio_is_valid(cs_gpio)) { - if (gpio_request(cs_gpio, "ssp-pl022")) + if (devm_gpio_request(dev, cs_gpio, "ssp-pl022")) dev_err(&adev->dev, "could not request %d gpio\n", cs_gpio); @@ -2180,7 +2177,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) goto err_no_ioregion; pl022->phybase = adev->res.start; - pl022->virtbase = ioremap(adev->res.start, resource_size(&adev->res)); + pl022->virtbase = devm_ioremap(dev, adev->res.start, + resource_size(&adev->res)); if (pl022->virtbase == NULL) { status = -ENOMEM; goto err_no_ioremap; @@ -2190,7 +2188,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pm_runtime_resume(dev); - pl022->clk = clk_get(&adev->dev, NULL); + pl022->clk = devm_clk_get(&adev->dev, NULL); if (IS_ERR(pl022->clk)) { status = PTR_ERR(pl022->clk); dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n"); @@ -2218,8 +2216,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) SSP_CR1(pl022->virtbase)); load_ssp_default_config(pl022); - status = request_irq(adev->irq[0], pl022_interrupt_handler, 0, "pl022", - pl022); + status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler, + 0, "pl022", pl022); if (status < 0) { dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status); goto err_no_irq; @@ -2259,24 +2257,18 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) err_spi_register: if (platform_info->enable_dma) pl022_dma_remove(pl022); - - free_irq(adev->irq[0], pl022); err_no_irq: clk_disable(pl022->clk); err_no_clk_en: clk_unprepare(pl022->clk); err_clk_prep: - clk_put(pl022->clk); err_no_clk: - iounmap(pl022->virtbase); err_no_ioremap: amba_release_regions(adev); err_no_ioregion: err_no_gpio: err_no_pinctrl: spi_master_put(master); - err_no_master: - err_no_pdata: return status; } @@ -2298,12 +2290,9 @@ pl022_remove(struct amba_device *adev) if (pl022->master_info->enable_dma) pl022_dma_remove(pl022); - free_irq(adev->irq[0], pl022); clk_disable(pl022->clk); clk_unprepare(pl022->clk); - clk_put(pl022->clk); pm_runtime_disable(&adev->dev); - iounmap(pl022->virtbase); amba_release_regions(adev); tasklet_disable(&pl022->pump_transfers); spi_unregister_master(pl022->master); -- cgit v1.2.3 From ada7aec7eec0ca2d2a9d2208548813d5dda8108e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 26 Sep 2012 18:06:22 +0200 Subject: spi/pl022: get/put resources on suspend/resume This factors out the resource handling in runtime suspend/resume and also calls it from the ordinary suspend and resume hooks. The semantics require that ordinary PM op suspend is called with runtime PM in resumed mode, so that ordinary suspend can assume that it will e.g. decrease the clock reference counter to 0, runtime resume having previously increased it to 1. Cc: Vipul Kumar Samar Cc: Viresh Kumar Acked-by: Ulf Hansson Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 66 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 22 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 15737bcee9fb..919464102d33 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2300,6 +2300,45 @@ pl022_remove(struct amba_device *adev) return 0; } +#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME) +/* + * These two functions are used from both suspend/resume and + * the runtime counterparts to handle external resources like + * clocks, pins and regulators when going to sleep. + */ +static void pl022_suspend_resources(struct pl022 *pl022) +{ + int ret; + + clk_disable(pl022->clk); + + /* Optionally let pins go into sleep states */ + if (!IS_ERR(pl022->pins_sleep)) { + ret = pinctrl_select_state(pl022->pinctrl, + pl022->pins_sleep); + if (ret) + dev_err(&pl022->adev->dev, + "could not set pins to sleep state\n"); + } +} + +static void pl022_resume_resources(struct pl022 *pl022) +{ + int ret; + + /* Optionaly enable pins to be muxed in and configured */ + if (!IS_ERR(pl022->pins_default)) { + ret = pinctrl_select_state(pl022->pinctrl, + pl022->pins_default); + if (ret) + dev_err(&pl022->adev->dev, + "could not set default pins\n"); + } + + clk_enable(pl022->clk); +} +#endif + #ifdef CONFIG_SUSPEND static int pl022_suspend(struct device *dev) { @@ -2311,6 +2350,7 @@ static int pl022_suspend(struct device *dev) dev_warn(dev, "cannot suspend master\n"); return ret; } + pl022_suspend_resources(pl022); dev_dbg(dev, "suspended\n"); return 0; @@ -2321,6 +2361,8 @@ static int pl022_resume(struct device *dev) struct pl022 *pl022 = dev_get_drvdata(dev); int ret; + pl022_resume_resources(pl022); + /* Start the queue running */ ret = spi_master_resume(pl022->master); if (ret) @@ -2336,36 +2378,16 @@ static int pl022_resume(struct device *dev) static int pl022_runtime_suspend(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); - int status = 0; - - clk_disable(pl022->clk); - - /* Optionally let pins go into sleep states */ - if (!IS_ERR(pl022->pins_sleep)) { - status = pinctrl_select_state(pl022->pinctrl, - pl022->pins_sleep); - if (status) - dev_err(dev, "could not set pins to sleep state\n"); - } + pl022_suspend_resources(pl022); return 0; } static int pl022_runtime_resume(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); - int status = 0; - - /* Optionaly enable pins to be muxed in and configured */ - if (!IS_ERR(pl022->pins_default)) { - status = pinctrl_select_state(pl022->pinctrl, - pl022->pins_default); - if (status) - dev_err(dev, "could not set default pins\n"); - } - - clk_enable(pl022->clk); + pl022_resume_resources(pl022); return 0; } #endif -- cgit v1.2.3 From c09b890b763df3ccd79a2c34c2f1abeb73179caf Mon Sep 17 00:00:00 2001 From: Knut Wohlrab Date: Tue, 25 Sep 2012 13:21:57 +0200 Subject: spi/imx: set the inactive state of the clock according to the clock polarity There are SPI devices which need a SPI clock with active low polarity and high inactive state. Add the setting of the inactive state ECSPIx_CONFIGREG:SCLK CTL according to the clock polarity ECSPIx_CONFIGREG:SCLK POL: DT without "spi-cpol" = 0 = clock active high polarity = inactive state low DT with "spi-cpol" = 1 = clock active low polarity = inactive state high Signed-off-by: Knut Wohlrab Signed-off-by: Dirk Behme Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index e834ff8c0188..d64655b70b59 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -197,6 +197,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin, #define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4)) #define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8)) #define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs) + 12)) +#define MX51_ECSPI_CONFIG_SCLKCTL(cs) (1 << ((cs) + 20)) #define MX51_ECSPI_INT 0x10 #define MX51_ECSPI_INT_TEEN (1 << 0) @@ -287,9 +288,10 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, if (config->mode & SPI_CPHA) cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs); - if (config->mode & SPI_CPOL) + if (config->mode & SPI_CPOL) { cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs); - + cfg |= MX51_ECSPI_CONFIG_SCLKCTL(config->cs); + } if (config->mode & SPI_CS_HIGH) cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs); -- cgit v1.2.3