aboutsummaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-02 17:26:42 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-02 17:26:42 -0700
commit7fe0b14b725d6d09a1d9e1409bd465cb88b587f9 (patch)
tree89b5ffca03145618da92c75fb3cc1dda87dbb924 /drivers/spi
parent7a9a2970b5c1c2ce73d4bb84edaa7ebf13e0c841 (diff)
parent536a53a300d0d40152796eefb0a9e6e36ca37f7d (diff)
Merge tag 'spi-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/misc
Pull spi updates from Mark Brown: "No framework work here, only a bunch of driver updates of varying sizes: - Factoring out of the core hardware support from the MXS MMC driver by Marek Vasut to allow the hardware to also be used for SPI. - Lots of error handling cleanups from Guenter Roeck - Removal of the existing Tegra driver which is quite comprehensively broken as detailed in the changelog for the removal. - DT suppport for the PL022 and GPIO drivers. - pinctrl support for OMAP and PL022." Pulling from Mark Brown as Grant Likely is still busy moving. * tag 'spi-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/misc: (53 commits) spi: remove completely broken Tegra driver spi/imx: set the inactive state of the clock according to the clock polarity spi/pl022: get/put resources on suspend/resume spi/pl022: use more managed resources spi/pl022: Devicetree support w/o platform data spi/s3c64xx: Don't free controller_data on non-dt platforms spi: omap2-mcspi: add pinctrl support spi/pl022: adopt pinctrl support spi: omap2-mcspi: Cleanup the omap2_mcspi_txrx_dma function spi/gpio: Fix stub for spi_gpio_probe_dt() spi/mxs: Make the SPI block clock speed configurable via DT spi: spi-sh-hspi: drop frees of devm_ alloc'd data spi/pl022: Fix chipselects pointer computation spi: spi-tle62x0: Use module_spi_driver macro mxs/spi: Rework the mxs_ssp_timeout to be more readable mxs/spi: Decrement the DMA/PIO border mxs/spi: Increment the transfer length only if transfer succeeded mxs/spi: Fix issues when doing long continuous transfer spi: spi-gpio: Add DT bindings spi: spi-gpio: store chipselect information in private structure ...
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig15
-rw-r--r--drivers/spi/Makefile3
-rw-r--r--drivers/spi/spi-altera.c4
-rw-r--r--drivers/spi/spi-gpio.c131
-rw-r--r--drivers/spi/spi-imx.c6
-rw-r--r--drivers/spi/spi-mpc512x-psc.c3
-rw-r--r--drivers/spi/spi-mpc52xx-psc.c3
-rw-r--r--drivers/spi/spi-mpc52xx.c9
-rw-r--r--drivers/spi/spi-mxs.c674
-rw-r--r--drivers/spi/spi-omap2-mcspi.c321
-rw-r--r--drivers/spi/spi-orion.c209
-rw-r--r--drivers/spi/spi-pl022.c251
-rw-r--r--drivers/spi/spi-s3c24xx.c1
-rw-r--r--drivers/spi/spi-s3c64xx.c11
-rw-r--r--drivers/spi/spi-sc18is602.c364
-rw-r--r--drivers/spi/spi-sh-hspi.c5
-rw-r--r--drivers/spi/spi-stmp.c6
-rw-r--r--drivers/spi/spi-tegra.c647
-rw-r--r--drivers/spi/spi-tle62x0.c13
-rw-r--r--drivers/spi/spi-topcliff-pch.c2
20 files changed, 1639 insertions, 1039 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 2d198a01a41..ecc31a1f73f 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
@@ -364,11 +370,12 @@ config SPI_STMP3XXX
help
SPI driver for Freescale STMP37xx/378x SoC SSP interface
-config SPI_TEGRA
- tristate "Nvidia Tegra SPI controller"
- depends on ARCH_TEGRA && TEGRA20_APB_DMA
+config SPI_MXS
+ tristate "Freescale MXS SPI controller"
+ depends on ARCH_MXS
+ select STMP_DEVICE
help
- SPI driver for NVidia Tegra SoCs
+ SPI driver for Freescale MXS devices.
config SPI_TI_SSP
tristate "TI Sequencer Serial Port - SPI Support"
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 3920dcf4c74..22fd3a7251b 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
@@ -51,13 +52,13 @@ 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
obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
obj-$(CONFIG_SPI_SIRF) += spi-sirf.o
obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o
-obj-$(CONFIG_SPI_TEGRA) += spi-tegra.o
obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c
index c00d00e96ee..f1fec2a19d1 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);
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index 0b56cfc71fa..a2b50c516b3 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -22,6 +22,8 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
@@ -46,6 +48,7 @@ struct spi_gpio {
struct spi_bitbang bitbang;
struct spi_gpio_platform_data pdata;
struct platform_device *pdev;
+ int cs_gpios[0];
};
/*----------------------------------------------------------------------*/
@@ -89,15 +92,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 +219,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,12 +234,27 @@ 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;
+ 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));
@@ -239,8 +264,12 @@ static int spi_gpio_setup(struct spi_device *spi)
!(spi->mode & SPI_CS_HIGH));
}
}
- if (!status)
+ if (!status) {
status = spi_bitbang_setup(spi);
+ /* in case it was initialized from static board data */
+ spi_gpio->cs_gpios[spi->chip_select] = cs;
+ }
+
if (status) {
if (!spi->controller_state && cs != SPI_GPIO_NO_CHIPSELECT)
gpio_free(cs);
@@ -250,7 +279,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);
@@ -313,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_gpio_probe_dt(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
static int __devinit spi_gpio_probe(struct platform_device *pdev)
{
int status;
@@ -320,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
@@ -331,7 +417,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;
@@ -348,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;
@@ -408,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),
};
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index de7ebb65e53..c9a0d8467de 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);
diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c
index 4c63f772780..0a1e39e94d0 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;
}
diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c
index 66047156d90..bd47d262d53 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;
}
diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c
index cb3a3830b0a..04541065021 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);
@@ -528,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;
@@ -540,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;
}
diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c
new file mode 100644
index 00000000000..edf1360ab09
--- /dev/null
+++ b/drivers/spi/spi-mxs.c
@@ -0,0 +1,674 @@
+/*
+ * 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 <marex@denx.de>
+ *
+ * Based on previous attempt by:
+ * Fabio Estevam <fabio.estevam@freescale.com>
+ *
+ * Based on code from U-Boot bootloader by:
+ * Marek Vasut <marex@denx.de>
+ *
+ * Based on spi-stmp.c, which is:
+ * Author: Dmitry Pervushin <dimka@embeddedalley.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/highmem.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/stmp_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mxs-spi.h>
+
+#define DRIVER_NAME "mxs-spi"
+
+/* Use 10S timeout for very long transfers, it should suffice. */
+#define SSP_TIMEOUT 10000
+
+#define SG_MAXLEN 0xff00
+
+struct mxs_spi {
+ struct mxs_ssp ssp;
+ struct completion c;
+};
+
+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)
+{
+ const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT);
+ struct mxs_ssp *ssp = &spi->ssp;
+ uint32_t reg;
+
+ do {
+ reg = readl_relaxed(ssp->base + offset);
+
+ if (!set)
+ reg = ~reg;
+
+ reg &= mask;
+
+ if (reg == mask)
+ return 0;
+ } while (time_before(jiffies, timeout));
+
+ return -ETIMEDOUT;
+}
+
+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 = 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;
+ 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)
+ ctrl0 |= BM_SSP_CTRL0_LOCK_CS;
+ if (!write)
+ ctrl0 |= BM_SSP_CTRL0_READ;
+
+ /* Queue the DMA data transfer. */
+ for (sg_count = 0; sg_count < sgs; sg_count++) {
+ 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;
+ }
+ }
+
+ /*
+ * 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_vmalloc;
+ }
+
+ ret = 0;
+
+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;
+}
+
+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) || (t->rx_dma && t->tx_dma)) {
+ dev_err(ssp->dev,
+ "Cannot send and receive simultaneously\n");
+ status = -EINVAL;
+ break;
+ }
+
+ /*
+ * Small blocks can be transfered via PIO.
+ * 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 < 32) {
+ 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);
+ }
+
+ if (status) {
+ stmp_reset_block(ssp->base);
+ break;
+ }
+
+ m->actual_length += t->len;
+ first = last = 0;
+ }
+
+ m->status = 0;
+ spi_finalize_current_message(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, },
+ { /* sent