diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-12-15 14:52:16 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-12-15 14:52:16 -0300 |
commit | 8d91c1e411f55d7ea91b1183a2e9f8088fb4d5be (patch) | |
tree | e9891aa6c295060d065adffd610c4f49ecf884f3 /drivers/spi | |
parent | a71852147516bc1cb5b0b3cbd13639bfd4022dc8 (diff) |
Linux-libre 4.3.2-gnu
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 20 | ||||
-rw-r--r-- | drivers/spi/Makefile | 2 | ||||
-rw-r--r-- | drivers/spi/spi-atmel.c | 3 | ||||
-rw-r--r-- | drivers/spi/spi-bcm2835.c | 10 | ||||
-rw-r--r-- | drivers/spi/spi-bcm63xx-hsspi.c | 13 | ||||
-rw-r--r-- | drivers/spi/spi-davinci.c | 57 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-espi.c | 89 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-lib.c | 19 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-lib.h | 3 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-spi.c | 43 | ||||
-rw-r--r-- | drivers/spi/spi-img-spfi.c | 14 | ||||
-rw-r--r-- | drivers/spi/spi-meson-spifc.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-mpc512x-psc.c | 70 | ||||
-rw-r--r-- | drivers/spi/spi-mt65xx.c | 719 | ||||
-rw-r--r-- | drivers/spi/spi-pxa2xx-pci.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-pxa2xx.c | 66 | ||||
-rw-r--r-- | drivers/spi/spi-pxa2xx.h | 5 | ||||
-rw-r--r-- | drivers/spi/spi-rockchip.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-rspi.c | 19 | ||||
-rw-r--r-- | drivers/spi/spi-s3c24xx.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-s3c64xx.c | 4 | ||||
-rw-r--r-- | drivers/spi/spi-sh-msiof.c | 5 | ||||
-rw-r--r-- | drivers/spi/spi-ti-qspi.c | 34 | ||||
-rw-r--r-- | drivers/spi/spi-xcomm.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-xlp.c | 456 | ||||
-rw-r--r-- | drivers/spi/spi.c | 235 | ||||
-rw-r--r-- | drivers/spi/spidev.c | 8 |
27 files changed, 1713 insertions, 187 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b0f30fb68..4887f317e 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -326,6 +326,15 @@ config SPI_MESON_SPIFC This enables master mode support for the SPIFC (SPI flash controller) available in Amlogic Meson SoCs. +config SPI_MT65XX + tristate "MediaTek SPI controller" + depends on ARCH_MEDIATEK || COMPILE_TEST + help + This selects the MediaTek(R) SPI bus driver. + If you want to use MediaTek(R) SPI interface, + say Y or M here.If you are not sure, say N. + SPI drivers for Mediatek MT65XX and MT81XX series ARM SoCs. + config SPI_OC_TINY tristate "OpenCores tiny SPI" depends on GPIOLIB || COMPILE_TEST @@ -598,6 +607,17 @@ config SPI_XILINX Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)" +config SPI_XLP + tristate "Netlogic XLP SPI controller driver" + depends on CPU_XLP || COMPILE_TEST + help + Enable support for the SPI controller on the Netlogic XLP SoCs. + Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX, XLP9XX + and XLP5XX. + + If you have a Netlogic XLP platform say Y here. + If unsure, say N. + config SPI_XTENSA_XTFPGA tristate "Xtensa SPI controller for xtfpga" depends on (XTENSA && XTENSA_PLATFORM_XTFPGA) || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 1154dbac8..6a7f6f9d0 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.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_MT65XX) += spi-mt65xx.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o @@ -88,5 +89,6 @@ obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o +obj-$(CONFIG_SPI_XLP) += spi-xlp.o obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index c9eca3477..63318e2af 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -19,7 +19,6 @@ #include <linux/interrupt.h> #include <linux/spi/spi.h> #include <linux/slab.h> -#include <linux/platform_data/atmel.h> #include <linux/platform_data/dma-atmel.h> #include <linux/of.h> @@ -1721,6 +1720,7 @@ static int atmel_spi_runtime_resume(struct device *dev) return clk_prepare_enable(as->clk); } +#ifdef CONFIG_PM_SLEEP static int atmel_spi_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); @@ -1757,6 +1757,7 @@ static int atmel_spi_resume(struct device *dev) return ret; } +#endif static const struct dev_pm_ops atmel_spi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(atmel_spi_suspend, atmel_spi_resume) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 744596464..3e8eeb23d 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -480,7 +480,7 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr, u32 cs, - unsigned long xfer_time_us) + unsigned long long xfer_time_us) { struct bcm2835_spi *bs = spi_master_get_devdata(master); unsigned long timeout; @@ -531,7 +531,8 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, { struct bcm2835_spi *bs = spi_master_get_devdata(master); unsigned long spi_hz, clk_hz, cdiv; - unsigned long spi_used_hz, xfer_time_us; + unsigned long spi_used_hz; + unsigned long long xfer_time_us; u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); /* set clock */ @@ -573,9 +574,10 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, bs->rx_len = tfr->len; /* calculate the estimated time in us the transfer runs */ - xfer_time_us = tfr->len + xfer_time_us = (unsigned long long)tfr->len * 9 /* clocks/byte - SPI-HW waits 1 clock after each byte */ - * 1000000 / spi_used_hz; + * 1000000; + do_div(xfer_time_us, spi_used_hz); /* for short requests run polling*/ if (xfer_time_us <= BCM2835_SPI_POLLING_LIMIT_US) diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index f5ca6dc3a..55789f7cd 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -76,6 +76,7 @@ #define HSSPI_FIFO_REG(x) (0x200 + (x) * 0x200) +#define HSSPI_OP_MULTIBIT BIT(11) #define HSSPI_OP_CODE_SHIFT 13 #define HSSPI_OP_SLEEP (0 << HSSPI_OP_CODE_SHIFT) #define HSSPI_OP_READ_WRITE (1 << HSSPI_OP_CODE_SHIFT) @@ -171,9 +172,12 @@ static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t) if (opcode != HSSPI_OP_READ) step_size -= HSSPI_OPCODE_LEN; - __raw_writel(0 << MODE_CTRL_PREPENDBYTE_CNT_SHIFT | - 2 << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT | - 2 << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT | 0xff, + if ((opcode == HSSPI_OP_READ && t->rx_nbits == SPI_NBITS_DUAL) || + (opcode == HSSPI_OP_WRITE && t->tx_nbits == SPI_NBITS_DUAL)) + opcode |= HSSPI_OP_MULTIBIT; + + __raw_writel(1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT | + 1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT | 0xff, bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select)); while (pending > 0) { @@ -374,7 +378,8 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) master->num_chipselect = 8; master->setup = bcm63xx_hsspi_setup; master->transfer_one_message = bcm63xx_hsspi_transfer_one; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | + SPI_RX_DUAL | SPI_TX_DUAL; master->bits_per_word_mask = SPI_BPW_MASK(8); master->auto_runtime_pm = true; diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 987afebea..a85d863d4 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -139,6 +139,8 @@ struct davinci_spi { u32 (*get_tx)(struct davinci_spi *); u8 *bytes_per_word; + + u8 prescaler_limit; }; static struct davinci_spi_config davinci_spi_default_cfg; @@ -255,7 +257,7 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) * This function calculates the prescale value that generates a clock rate * less than or equal to the specified maximum. * - * Returns: calculated prescale - 1 for easy programming into SPI registers + * Returns: calculated prescale value for easy programming into SPI registers * or negative error number if valid prescalar cannot be updated. */ static inline int davinci_spi_get_prescale(struct davinci_spi *dspi, @@ -263,12 +265,13 @@ static inline int davinci_spi_get_prescale(struct davinci_spi *dspi, { int ret; - ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz); + /* Subtract 1 to match what will be programmed into SPI register. */ + ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz) - 1; - if (ret < 1 || ret > 256) + if (ret < dspi->prescaler_limit || ret > 255) return -EINVAL; - return ret - 1; + return ret; } /** @@ -832,13 +835,40 @@ rx_dma_failed: } #if defined(CONFIG_OF) + +/* OF SPI data structure */ +struct davinci_spi_of_data { + u8 version; + u8 prescaler_limit; +}; + +static const struct davinci_spi_of_data dm6441_spi_data = { + .version = SPI_VERSION_1, + .prescaler_limit = 2, +}; + +static const struct davinci_spi_of_data da830_spi_data = { + .version = SPI_VERSION_2, + .prescaler_limit = 2, +}; + +static const struct davinci_spi_of_data keystone_spi_data = { + .version = SPI_VERSION_1, + .prescaler_limit = 0, +}; + static const struct of_device_id davinci_spi_of_match[] = { { .compatible = "ti,dm6441-spi", + .data = &dm6441_spi_data, }, { .compatible = "ti,da830-spi", - .data = (void *)SPI_VERSION_2, + .data = &da830_spi_data, + }, + { + .compatible = "ti,keystone-spi", + .data = &keystone_spi_data, }, { }, }; @@ -857,21 +887,21 @@ static int spi_davinci_get_pdata(struct platform_device *pdev, struct davinci_spi *dspi) { struct device_node *node = pdev->dev.of_node; + struct davinci_spi_of_data *spi_data; struct davinci_spi_platform_data *pdata; unsigned int num_cs, intr_line = 0; const struct of_device_id *match; pdata = &dspi->pdata; - pdata->version = SPI_VERSION_1; match = of_match_device(davinci_spi_of_match, &pdev->dev); if (!match) return -ENODEV; - /* match data has the SPI version number for SPI_VERSION_2 */ - if (match->data == (void *)SPI_VERSION_2) - pdata->version = SPI_VERSION_2; + spi_data = (struct davinci_spi_of_data *)match->data; + pdata->version = spi_data->version; + pdata->prescaler_limit = spi_data->prescaler_limit; /* * default num_cs is 1 and all chipsel are internal to the chip * indicated by chip_sel being NULL or cs_gpios being NULL or @@ -962,11 +992,12 @@ static int davinci_spi_probe(struct platform_device *pdev) goto free_master; } - dspi->irq = platform_get_irq(pdev, 0); - if (dspi->irq <= 0) { + ret = platform_get_irq(pdev, 0); + if (ret == 0) ret = -EINVAL; + if (ret < 0) goto free_master; - } + dspi->irq = ret; ret = devm_request_threaded_irq(&pdev->dev, dspi->irq, davinci_spi_irq, dummy_thread_fn, 0, dev_name(&pdev->dev), dspi); @@ -991,7 +1022,7 @@ static int davinci_spi_probe(struct platform_device *pdev) dspi->bitbang.chipselect = davinci_spi_chipselect; dspi->bitbang.setup_transfer = davinci_spi_setup_transfer; - + dspi->prescaler_limit = pdata->prescaler_limit; dspi->version = pdata->version; dspi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP; diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index d3f05a052..c27124a5e 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -21,6 +21,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> +#include <linux/pm_runtime.h> #include <sysdev/fsl_soc.h> #include "spi-fsl-lib.h" @@ -85,6 +86,8 @@ struct fsl_espi_transfer { #define SPCOM_TRANLEN(x) ((x) << 0) #define SPCOM_TRANLEN_MAX 0xFFFF /* Max transaction length */ +#define AUTOSUSPEND_TIMEOUT 2000 + static void fsl_espi_change_mode(struct spi_device *spi) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); @@ -485,6 +488,8 @@ static int fsl_espi_setup(struct spi_device *spi) mpc8xxx_spi = spi_master_get_devdata(spi->master); reg_base = mpc8xxx_spi->reg_base; + pm_runtime_get_sync(mpc8xxx_spi->dev); + hw_mode = cs->hw_mode; /* Save original settings */ cs->hw_mode = mpc8xxx_spi_read_reg( ®_base->csmode[spi->chip_select]); @@ -507,6 +512,10 @@ static int fsl_espi_setup(struct spi_device *spi) mpc8xxx_spi_write_reg(®_base->mode, loop_mode); retval = fsl_espi_setup_transfer(spi, NULL); + + pm_runtime_mark_last_busy(mpc8xxx_spi->dev); + pm_runtime_put_autosuspend(mpc8xxx_spi->dev); + if (retval < 0) { cs->hw_mode = hw_mode; /* Restore settings */ return retval; @@ -604,20 +613,14 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) return ret; } -static void fsl_espi_remove(struct mpc8xxx_spi *mspi) +#ifdef CONFIG_PM +static int fsl_espi_runtime_suspend(struct device *dev) { - iounmap(mspi->reg_base); -} - -static int fsl_espi_suspend(struct spi_master *master) -{ - struct mpc8xxx_spi *mpc8xxx_spi; - struct fsl_espi_reg *reg_base; + struct spi_master *master = dev_get_drvdata(dev); + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); + struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base; u32 regval; - mpc8xxx_spi = spi_master_get_devdata(master); - reg_base = mpc8xxx_spi->reg_base; - regval = mpc8xxx_spi_read_reg(®_base->mode); regval &= ~SPMODE_ENABLE; mpc8xxx_spi_write_reg(®_base->mode, regval); @@ -625,21 +628,20 @@ static int fsl_espi_suspend(struct spi_master *master) return 0; } -static int fsl_espi_resume(struct spi_master *master) +static int fsl_espi_runtime_resume(struct device *dev) { - struct mpc8xxx_spi *mpc8xxx_spi; - struct fsl_espi_reg *reg_base; + struct spi_master *master = dev_get_drvdata(dev); + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); + struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base; u32 regval; - mpc8xxx_spi = spi_master_get_devdata(master); - reg_base = mpc8xxx_spi->reg_base; - regval = mpc8xxx_spi_read_reg(®_base->mode); regval |= SPMODE_ENABLE; mpc8xxx_spi_write_reg(®_base->mode, regval); return 0; } +#endif static struct spi_master * fsl_espi_probe(struct device *dev, struct resource *mem, unsigned int irq) @@ -667,25 +669,23 @@ static struct spi_master * fsl_espi_probe(struct device *dev, master->setup = fsl_espi_setup; master->cleanup = fsl_espi_cleanup; master->transfer_one_message = fsl_espi_do_one_msg; - master->prepare_transfer_hardware = fsl_espi_resume; - master->unprepare_transfer_hardware = fsl_espi_suspend; + master->auto_runtime_pm = true; mpc8xxx_spi = spi_master_get_devdata(master); - mpc8xxx_spi->spi_remove = fsl_espi_remove; - mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem)); - if (!mpc8xxx_spi->reg_base) { - ret = -ENOMEM; + mpc8xxx_spi->reg_base = devm_ioremap_resource(dev, mem); + if (IS_ERR(mpc8xxx_spi->reg_base)) { + ret = PTR_ERR(mpc8xxx_spi->reg_base); goto err_probe; } reg_base = mpc8xxx_spi->reg_base; /* Register for SPI Interrupt */ - ret = request_irq(mpc8xxx_spi->irq, fsl_espi_irq, + ret = devm_request_irq(dev, mpc8xxx_spi->irq, fsl_espi_irq, 0, "fsl_espi", mpc8xxx_spi); if (ret) - goto free_irq; + goto err_probe; if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { mpc8xxx_spi->rx_shift = 16; @@ -731,18 +731,27 @@ static struct spi_master * fsl_espi_probe(struct device *dev, mpc8xxx_spi_write_reg(®_base->mode, regval); - ret = spi_register_master(master); + pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + ret = devm_spi_register_master(dev, master); if (ret < 0) - goto unreg_master; + goto err_pm; dev_info(dev, "at 0x%p (irq = %d)\n", reg_base, mpc8xxx_spi->irq); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return master; -unreg_master: - free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); -free_irq: - iounmap(mpc8xxx_spi->reg_base); +err_pm: + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); err_probe: spi_master_put(master); err: @@ -809,7 +818,9 @@ err: static int of_fsl_espi_remove(struct platform_device *dev) { - return mpc8xxx_spi_remove(&dev->dev); + pm_runtime_disable(&dev->dev); + + return 0; } #ifdef CONFIG_PM_SLEEP @@ -824,7 +835,11 @@ static int of_fsl_espi_suspend(struct device *dev) return ret; } - return fsl_espi_suspend(master); + ret = pm_runtime_force_suspend(dev); + if (ret < 0) + return ret; + + return 0; } static int of_fsl_espi_resume(struct device *dev) @@ -834,7 +849,7 @@ static int of_fsl_espi_resume(struct device *dev) struct mpc8xxx_spi *mpc8xxx_spi; struct fsl_espi_reg *reg_base; u32 regval; - int i; + int i, ret; mpc8xxx_spi = spi_master_get_devdata(master); reg_base = mpc8xxx_spi->reg_base; @@ -854,11 +869,17 @@ static int of_fsl_espi_resume(struct device *dev) mpc8xxx_spi_write_reg(®_base->mode, regval); + ret = pm_runtime_force_resume(dev); + if (ret < 0) + return ret; + return spi_master_resume(master); } #endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops espi_pm = { + SET_RUNTIME_PM_OPS(fsl_espi_runtime_suspend, + fsl_espi_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(of_fsl_espi_suspend, of_fsl_espi_resume) }; diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index cb35d2f0d..1e43412cd 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -114,25 +114,6 @@ void mpc8xxx_spi_probe(struct device *dev, struct resource *mem, } EXPORT_SYMBOL_GPL(mpc8xxx_spi_probe); -int mpc8xxx_spi_remove(struct device *dev) -{ - struct mpc8xxx_spi *mpc8xxx_spi; - struct spi_master *master; - - master = dev_get_drvdata(dev); - mpc8xxx_spi = spi_master_get_devdata(master); - - spi_unregister_master(master); - - free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); - - if (mpc8xxx_spi->spi_remove) - mpc8xxx_spi->spi_remove(mpc8xxx_spi); - - return 0; -} -EXPORT_SYMBOL_GPL(mpc8xxx_spi_remove); - int of_mpc8xxx_spi_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 1326a392a..84f5dcb7a 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -54,9 +54,6 @@ struct mpc8xxx_spi { void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *); u32(*get_tx) (struct mpc8xxx_spi *); - /* hooks for different controller driver */ - void (*spi_remove) (struct mpc8xxx_spi *mspi); - unsigned int count; unsigned int irq; diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 60c590790..8b290d9d7 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -559,12 +559,6 @@ static irqreturn_t fsl_spi_irq(s32 irq, void *context_data) return ret; } -static void fsl_spi_remove(struct mpc8xxx_spi *mspi) -{ - iounmap(mspi->reg_base); - fsl_spi_cpm_free(mspi); -} - static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on) { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); @@ -631,7 +625,6 @@ static struct spi_master * fsl_spi_probe(struct device *dev, master->transfer_one_message = fsl_spi_do_one_msg; mpc8xxx_spi = spi_master_get_devdata(master); - mpc8xxx_spi->spi_remove = fsl_spi_remove; mpc8xxx_spi->max_bits_per_word = 32; mpc8xxx_spi->type = fsl_spi_get_type(dev); @@ -639,10 +632,10 @@ static struct spi_master * fsl_spi_probe(struct device *dev, if (ret) goto err_cpm_init; - mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem)); - if (mpc8xxx_spi->reg_base == NULL) { - ret = -ENOMEM; - goto err_ioremap; + mpc8xxx_spi->reg_base = devm_ioremap_resource(dev, mem); + if (IS_ERR(mpc8xxx_spi->reg_base)) { + ret = PTR_ERR(mpc8xxx_spi->reg_base); + goto err_probe; } if (mpc8xxx_spi->type == TYPE_GRLIB) @@ -661,11 +654,11 @@ static struct spi_master * fsl_spi_probe(struct device *dev, &mpc8xxx_spi->tx_shift, 8, 1); /* Register for SPI Interrupt */ - ret = request_irq(mpc8xxx_spi->irq, fsl_spi_irq, - 0, "fsl_spi", mpc8xxx_spi); + ret = devm_request_irq(dev, mpc8xxx_spi->irq, fsl_spi_irq, + 0, "fsl_spi", mpc8xxx_spi); if (ret != 0) - goto free_irq; + goto err_probe; reg_base = mpc8xxx_spi->reg_base; @@ -686,20 +679,16 @@ static struct spi_master * fsl_spi_probe(struct device *dev, mpc8xxx_spi_write_reg(®_base->mode, regval); - ret = spi_register_master(master); + ret = devm_spi_register_master(dev, master); if (ret < 0) - goto unreg_master; + goto err_probe; dev_info(dev, "at 0x%p (irq = %d), %s mode\n", reg_base, mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags)); return master; -unreg_master: - free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); -free_irq: - iounmap(mpc8xxx_spi->reg_base); -err_ioremap: +err_probe: fsl_spi_cpm_free(mpc8xxx_spi); err_cpm_init: spi_master_put(master); @@ -866,11 +855,8 @@ static int of_fsl_spi_remove(struct platform_device *ofdev) { struct spi_master *master = platform_get_drvdata(ofdev); struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); - int ret; - ret = mpc8xxx_spi_remove(&ofdev->dev); - if (ret) - return ret; + fsl_spi_cpm_free(mpc8xxx_spi); if (mpc8xxx_spi->type == TYPE_FSL) of_fsl_spi_free_chipselects(&ofdev->dev); return 0; @@ -916,7 +902,12 @@ static int plat_mpc8xxx_spi_probe(struct platform_device *pdev) static int plat_mpc8xxx_spi_remove(struct platform_device *pdev) { - return mpc8xxx_spi_remove(&pdev->dev); + struct spi_master *master = platform_get_drvdata(pdev); + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); + + fsl_spi_cpm_free(mpc8xxx_spi); + + return 0; } MODULE_ALIAS("platform:mpc8xxx_spi"); diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index bb916c8d4..823cbc92d 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -581,6 +581,7 @@ static int img_spfi_probe(struct platform_device *pdev) struct img_spfi *spfi; struct resource *res; int ret; + u32 max_speed_hz; master = spi_alloc_master(&pdev->dev, sizeof(*spfi)); if (!master) @@ -645,6 +646,19 @@ static int img_spfi_probe(struct platform_device *pdev) master->max_speed_hz = clk_get_rate(spfi->spfi_clk) / 4; master->min_speed_hz = clk_get_rate(spfi->spfi_clk) / 512; + /* + * Maximum speed supported by spfi is limited to the lower value + * between 1/4 of the SPFI clock or to "spfi-max-frequency" + * defined in the device tree. + * If no value is defined in the device tree assume the maximum + * speed supported to be 1/4 of the SPFI clock. + */ + if (!of_property_read_u32(spfi->dev->of_node, "spfi-max-frequency", + &max_speed_hz)) { + if (master->max_speed_hz > max_speed_hz) + master->max_speed_hz = max_speed_hz; + } + master->setup = img_spfi_setup; master->cleanup = img_spfi_cleanup; master->transfer_one = img_spfi_transfer_one; diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index 5468fc70d..2465259f6 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -444,6 +444,7 @@ static const struct of_device_id meson_spifc_dt_match[] = { { .compatible = "amlogic,meson6-spifc", }, { }, }; +MODULE_DEVICE_TABLE(of, meson_spifc_dt_match); static struct platform_driver meson_spifc_driver = { .probe = meson_spifc_probe, diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 965d2bdcf..1e7534168 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -30,11 +30,37 @@ #include <linux/gpio.h> #include <asm/mpc52xx_psc.h> +enum { + TYPE_MPC5121, + TYPE_MPC5125, +}; + +/* + * This macro abstracts the differences in the PSC register layout between + * MPC5121 (which uses a struct mpc52xx_psc) and MPC5125 (using mpc5125_psc). + */ +#define psc_addr(mps, regname) ({ \ + void *__ret = NULL; \ + switch (mps->type) { \ + case TYPE_MPC5121: { \ + struct mpc52xx_psc __iomem *psc = mps->psc; \ + __ret = &psc->regname; \ + }; \ + break; \ + case TYPE_MPC5125: { \ + struct mpc5125_psc __iomem *psc = mps->psc; \ + __ret = &psc->regname; \ + }; \ + break; \ + } \ + __ret; }) + struct mpc512x_psc_spi { void (*cs_control)(struct spi_device *spi, bool on); /* driver internal data */ - struct mpc52xx_psc __iomem *psc; + int type; + void __iomem *psc; struct mpc512x_psc_fifo __iomem *fifo; unsigned int irq; u8 bits_per_word; @@ -71,13 +97,12 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi) { struct mpc512x_psc_spi_cs *cs = spi->controller_state; struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master); - struct mpc52xx_psc __iomem *psc = mps->psc; u32 sicr; u32 ccr; int speed; u16 bclkdiv; - sicr = in_be32(&psc->sicr); + sicr = in_be32(psc_addr(mps, sicr)); /* Set clock phase and polarity */ if (spi->mode & SPI_CPHA) @@ -94,9 +119,9 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi) sicr |= 0x10000000; else sicr &= ~0x10000000; - out_be32(&psc->sicr, sicr); + out_be32(psc_addr(mps, sicr), sicr); - ccr = in_be32(&psc->ccr); + ccr = in_be32(psc_addr(mps, ccr)); ccr &= 0xFF000000; speed = cs->speed_hz; if (!speed) @@ -104,7 +129,7 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi) bclkdiv = (mps->mclk_rate / speed) - 1; ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8)); - out_be32(&psc->ccr, ccr); + out_be32(psc_addr(mps, ccr), ccr); mps->bits_per_word = cs->bits_per_word; if (mps->cs_control && gpio_is_valid(spi->cs_gpio)) @@ -315,16 +340,15 @@ static int mpc512x_psc_spi_msg_xfer(struct spi_master *master, static int mpc512x_psc_spi_prep_xfer_hw(struct spi_master *master) { struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); - struct mpc52xx_psc __iomem *psc = mps->psc; dev_dbg(&master->dev, "%s()\n", __func__); /* Zero MR2 */ - in_8(&psc->mode); - out_8(&psc->mode, 0x0); + in_8(psc_addr(mps, mr2)); + out_8(psc_addr(mps, mr2), 0x0); /* enable transmitter/receiver */ - out_8(&psc->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); + out_8(psc_addr(mps, command), MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); return 0; } @@ -332,13 +356,12 @@ static int mpc512x_psc_spi_prep_xfer_hw(struct spi_master *master) static int mpc512x_psc_spi_unprep_xfer_hw(struct spi_master *master) { struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); - struct mpc52xx_psc __iomem *psc = mps->psc; struct mpc512x_psc_fifo __iomem *fifo = mps->fifo; dev_dbg(&master->dev, "%s()\n", __func__); /* disable transmitter/receiver and fifo interrupt */ - out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); + out_8(psc_addr(mps, command), MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); out_be32(&fifo->tximr, 0); return 0; @@ -388,7 +411,6 @@ static void mpc512x_psc_spi_cleanup(struct spi_device *spi) static int mpc512x_psc_spi_port_config(struct spi_master *master, struct mpc512x_psc_spi *mps) { - struct mpc52xx_psc __iomem *psc = mps->psc; struct mpc512x_psc_fifo __iomem *fifo = mps->fifo; u32 sicr; u32 ccr; @@ -396,12 +418,12 @@ static int mpc512x_psc_spi_port_config(struct spi_master *master, u16 bclkdiv; /* Reset the PSC into a known state */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); - out_8(&psc->command, MPC52xx_PSC_RST_TX); - out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); + out_8(psc_addr(mps, command), MPC52xx_PSC_RST_RX); + out_8(psc_addr(mps, command), MPC52xx_PSC_RST_TX); + out_8(psc_addr(mps, command), MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); /* Disable psc interrupts all useful interrupts are in fifo */ - out_be16(&psc->isr_imr.imr, 0); + out_be16(psc_addr(mps, isr_imr.imr), 0); /* Disable fifo interrupts, will be enabled later */ out_be32(&fifo->tximr, 0); @@ -417,18 +439,18 @@ static int mpc512x_psc_spi_port_config(struct spi_master *master, 0x00004000 | /* MSTR = 1 -- SPI master */ 0x00000800; /* UseEOF = 1 -- SS low until EOF */ - out_be32(&psc->sicr, sicr); + out_be32(psc_addr(mps, sicr), sicr); - ccr = in_be32(&psc->ccr); + ccr = in_be32(psc_addr(mps, ccr)); ccr &= 0xFF000000; speed = 1000000; /* default 1MHz */ bclkdiv = (mps->mclk_rate / speed) - 1; ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8)); - out_be32(&psc->ccr, ccr); + out_be32(psc_addr(mps, ccr), ccr); /* Set 2ms DTL delay */ - out_8(&psc->ctur, 0x00); - out_8(&psc->ctlr, 0x82); + out_8(psc_addr(mps, ctur), 0x00); + out_8(psc_addr(mps, ctlr), 0x82); /* we don't use the alarms */ out_be32(&fifo->rxalarm, 0xfff); @@ -482,6 +504,7 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, dev_set_drvdata(dev, master); mps = spi_master_get_devdata(master); + mps->type = (int)of_device_get_match_data(dev); mps->irq = irq; if (pdata == NULL) { @@ -589,7 +612,8 @@ static int mpc512x_psc_spi_of_remove(struct platform_device *op) } static const struct of_device_id mpc512x_psc_spi_of_match[] = { - { .compatible = "fsl,mpc5121-psc-spi", }, + { .compatible = "fsl,mpc5121-psc-spi", .data = (void *)TYPE_MPC5121 }, + { .compatible = "fsl,mpc5125-psc-spi", .data = (void *)TYPE_MPC5125 }, {}, }; diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c new file mode 100644 index 000000000..ecb6c5823 --- /dev/null +++ b/drivers/spi/spi-mt65xx.c @@ -0,0 +1,719 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: Leilk Liu <leilk.liu@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/platform_data/spi-mt65xx.h> +#include <linux/pm_runtime.h> +#include <linux/spi/spi.h> + +#define SPI_CFG0_REG 0x0000 +#define SPI_CFG1_REG 0x0004 +#define SPI_TX_SRC_REG 0x0008 +#define SPI_RX_DST_REG 0x000c +#define SPI_TX_DATA_REG 0x0010 +#define SPI_RX_DATA_REG 0x0014 +#define SPI_CMD_REG 0x0018 +#define SPI_STATUS0_REG 0x001c +#define SPI_PAD_SEL_REG 0x0024 + +#define SPI_CFG0_SCK_HIGH_OFFSET 0 +#define SPI_CFG0_SCK_LOW_OFFSET 8 +#define SPI_CFG0_CS_HOLD_OFFSET 16 +#define SPI_CFG0_CS_SETUP_OFFSET 24 + +#define SPI_CFG1_CS_IDLE_OFFSET 0 +#define SPI_CFG1_PACKET_LOOP_OFFSET 8 +#define SPI_CFG1_PACKET_LENGTH_OFFSET 16 +#define SPI_CFG1_GET_TICK_DLY_OFFSET 30 + +#define SPI_CFG1_CS_IDLE_MASK 0xff +#define SPI_CFG1_PACKET_LOOP_MASK 0xff00 +#define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000 + +#define SPI_CMD_ACT BIT(0) +#define SPI_CMD_RESUME BIT(1) +#define SPI_CMD_RST BIT(2) +#define SPI_CMD_PAUSE_EN BIT(4) +#define SPI_CMD_DEASSERT BIT(5) +#define SPI_CMD_CPHA BIT(8) +#define SPI_CMD_CPOL BIT(9) +#define SPI_CMD_RX_DMA BIT(10) +#define SPI_CMD_TX_DMA BIT(11) +#define SPI_CMD_TXMSBF BIT(12) +#define SPI_CMD_RXMSBF BIT(13) +#define SPI_CMD_RX_ENDIAN BIT(14) +#define SPI_CMD_TX_ENDIAN BIT(15) +#define SPI_CMD_FINISH_IE BIT(16) +#define SPI_CMD_PAUSE_IE BIT(17) + +#define MT8173_SPI_MAX_PAD_SEL 3 + +#define MTK_SPI_PAUSE_INT_STATUS 0x2 + +#define MTK_SPI_IDLE 0 +#define MTK_SPI_PAUSED 1 + +#define MTK_SPI_MAX_FIFO_SIZE 32 +#define MTK_SPI_PACKET_SIZE 1024 + +struct mtk_spi_compatible { + bool need_pad_sel; + /* Must explicitly send dummy Tx bytes to do Rx only transfer */ + bool must_tx; +}; + +struct mtk_spi { + void __iomem *base; + u32 state; + u32 pad_sel; + struct clk *parent_clk, *sel_clk, *spi_clk; + struct spi_transfer *cur_transfer; + u32 xfer_len; + struct scatterlist *tx_sgl, *rx_sgl; + u32 tx_sgl_len, rx_sgl_len; + const struct mtk_spi_compatible *dev_comp; +}; + +static const struct mtk_spi_compatible mt6589_compat; +static const struct mtk_spi_compatible mt8135_compat; +static const struct mtk_spi_compatible mt8173_compat = { + .need_pad_sel = true, + .must_tx = true, +}; + +/* + * A piece of default chip info unless the platform + * supplies it. + */ +static const struct mtk_chip_config mtk_default_chip_info = { + .rx_mlsb = 1, + .tx_mlsb = 1, +}; + +static const struct of_device_id mtk_spi_of_match[] = { + { .compatible = "mediatek,mt6589-spi", .data = (void *)&mt6589_compat }, + { .compatible = "mediatek,mt8135-spi", .data = (void *)&mt8135_compat }, + { .compatible = "mediatek,mt8173-spi", .data = (void *)&mt8173_compat }, + {} +}; +MODULE_DEVICE_TABLE(of, mtk_spi_of_match); + +static void mtk_spi_reset(struct mtk_spi *mdata) +{ + u32 reg_val; + + /* set the software reset bit in SPI_CMD_REG. */ + reg_val = readl(mdata->base + SPI_CMD_REG); + reg_val |= SPI_CMD_RST; + writel(reg_val, mdata->base + SPI_CMD_REG); + + reg_val = readl(mdata->base + SPI_CMD_REG); + reg_val &= ~SPI_CMD_RST; + writel(reg_val, mdata->base + SPI_CMD_REG); +} + +static void mtk_spi_config(struct mtk_spi *mdata, + struct mtk_chip_config *chip_config) +{ + u32 reg_val; + + reg_val = readl(mdata->base + SPI_CMD_REG); + + /* set the mlsbx and mlsbtx */ + if (chip_config->tx_mlsb) + reg_val |= SPI_CMD_TXMSBF; + else + reg_val &= ~SPI_CMD_TXMSBF; + if (chip_config->rx_mlsb) + reg_val |= SPI_CMD_RXMSBF; + else + reg_val &= ~SPI_CMD_RXMSBF; + + /* set the tx/rx endian */ +#ifdef __LITTLE_ENDIAN + reg_val &= ~SPI_CMD_TX_ENDIAN; + reg_val &= ~SPI_CMD_RX_ENDIAN; +#else + reg_val |= SPI_CMD_TX_ENDIAN; + reg_val |= SPI_CMD_RX_ENDIAN; +#endif + + /* set finish and pause interrupt always enable */ + reg_val |= SPI_CMD_FINISH_IE | SPI_CMD_PAUSE_IE; + + /* disable dma mode */ + reg_val &= ~(SPI_CMD_TX_DMA | SPI_CMD_RX_DMA); + + /* disable deassert mode */ + reg_val &= ~SPI_CMD_DEASSERT; + + writel(reg_val, mdata->base + SPI_CMD_REG); + + /* pad select */ + if (mdata->dev_comp->need_pad_sel) + writel(mdata->pad_sel, mdata->base + SPI_PAD_SEL_REG); +} + +static int mtk_spi_prepare_message(struct spi_master *master, + struct spi_message *msg) +{ + u32 reg_val; + u8 cpha, cpol; + struct mtk_chip_config *chip_config; + struct spi_device *spi = msg->spi; + struct mtk_spi *mdata = spi_master_get_devdata(master); + + cpha = spi->mode & SPI_CPHA ? 1 : 0; + cpol = spi->mode & SPI_CPOL ? 1 : 0; + + reg_val = readl(mdata->base + SPI_CMD_REG); + if (cpha) + reg_val |= SPI_CMD_CPHA; + else + reg_val &= ~SPI_CMD_CPHA; + if (cpol) + reg_val |= SPI_CMD_CPOL; + else + reg_val &= ~SPI_CMD_CPOL; + writel(reg_val, mdata->base + SPI_CMD_REG); + + chip_config = spi->controller_data; + if (!chip_config) { + chip_config = (void *)&mtk_default_chip_info; + spi->controller_data = chip_config; + } + mtk_spi_config(mdata, chip_config); + + return 0; +} + +static void mtk_spi_set_cs(struct spi_device *spi, bool enable) +{ + u32 reg_val; + struct mtk_spi *mdata = spi_master_get_devdata(spi->master); + + reg_val = readl(mdata->base + SPI_CMD_REG); + if (!enable) { + reg_val |= SPI_CMD_PAUSE_EN; + writel(reg_val, mdata->base + SPI_CMD_REG); + } else { + reg_val &= ~SPI_CMD_PAUSE_EN; + writel(reg_val, mdata->base + SPI_CMD_REG); + mdata->state = MTK_SPI_IDLE; + mtk_spi_reset(mdata); + } +} + +static void mtk_spi_prepare_transfer(struct spi_master *master, + struct spi_transfer *xfer) +{ + u32 spi_clk_hz, div, sck_time, cs_time, reg_val = 0; + struct mtk_spi *mdata = spi_master_get_devdata(master); + + spi_clk_hz = clk_get_rate(mdata->spi_clk); + if (xfer->speed_hz < spi_clk_hz / 2) + div = DIV_ROUND_UP(spi_clk_hz, xfer->speed_hz); + else + div = 1; + + sck_time = (div + 1) / 2; + cs_time = sck_time * 2; + + reg_val |= (((sck_time - 1) & 0xff) << SPI_CFG0_SCK_HIGH_OFFSET); + reg_val |= (((sck_time - 1) & 0xff) << SPI_CFG0_SCK_LOW_OFFSET); + reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET); + reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG0_CS_SETUP_OFFSET); + writel(reg_val, mdata->base + SPI_CFG0_REG); + + reg_val = readl(mdata->base + SPI_CFG1_REG); + reg_val &= ~SPI_CFG1_CS_IDLE_MASK; + reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET); + writel(reg_val, mdata->base + SPI_CFG1_REG); +} + +static void mtk_spi_setup_packet(struct spi_master *master) +{ + u32 packet_size, packet_loop, reg_val; + struct mtk_spi *mdata = spi_master_get_devdata(master); + + packet_size = min_t(u32, mdata->xfer_len, MTK_SPI_PACKET_SIZE); + packet_loop = mdata->xfer_len / packet_size; + + reg_val = readl(mdata->base + SPI_CFG1_REG); + reg_val &= ~(SPI_CFG1_PACKET_LENGTH_MASK | SPI_CFG1_PACKET_LOOP_MASK); + reg_val |= (packet_size - 1) << SPI_CFG1_PACKET_LENGTH_OFFSET; + reg_val |= (packet_loop - 1) << SPI_CFG1_PACKET_LOOP_OFFSET; + writel(reg_val, mdata->base + SPI_CFG1_REG); +} + +static void mtk_spi_enable_transfer(struct spi_master *master) +{ + u32 cmd; + struct mtk_spi *mdata = spi_master_get_devdata(master); + + cmd = readl(mdata->base + SPI_CMD_REG); + if (mdata->state == MTK_SPI_IDLE) + cmd |= SPI_CMD_ACT; + else + cmd |= SPI_CMD_RESUME; + writel(cmd, mdata->base + SPI_CMD_REG); +} + +static int mtk_spi_get_mult_delta(u32 xfer_len) +{ + u32 mult_delta; + + if (xfer_len > MTK_SPI_PACKET_SIZE) + mult_delta = xfer_len % MTK_SPI_PACKET_SIZE; + else + mult_delta = 0; + + return mult_delta; +} + +static void mtk_spi_update_mdata_len(struct spi_master *master) +{ + int mult_delta; + struct mtk_spi *mdata = spi_master_get_devdata(master); + + if (mdata->tx_sgl_len && mdata->rx_sgl_len) { + if (mdata->tx_sgl_len > mdata->rx_sgl_len) { + mult_delta = mtk_spi_get_mult_delta(mdata->rx_sgl_len); + mdata->xfer_len = mdata->rx_sgl_len - mult_delta; + mdata->rx_sgl_len = mult_delta; + mdata->tx_sgl_len -= mdata->xfer_len; + } else { + mult_delta = mtk_spi_get_mult_delta(mdata->tx_sgl_len); + mdata->xfer_len = mdata->tx_sgl_len - mult_delta; + mdata->tx_sgl_len = mult_delta; + mdata->rx_sgl_len -= mdata->xfer_len; + } + } else if (mdata->tx_sgl_len) { + mult_delta = mtk_spi_get_mult_delta(mdata->tx_sgl_len); + mdata->xfer_len = mdata->tx_sgl_len - mult_delta; + mdata->tx_sgl_len = mult_delta; + } else if (mdata->rx_sgl_len) { + mult_delta = mtk_spi_get_mult_delta(mdata->rx_sgl_len); + mdata->xfer_len = mdata->rx_sgl_len - mult_delta; + mdata->rx_sgl_len = mult_delta; + } +} + +static void mtk_spi_setup_dma_addr(struct spi_master *master, + struct spi_transfer *xfer) +{ + struct mtk_spi *mdata = spi_master_get_devdata(master); + + if (mdata->tx_sgl) + writel(xfer->tx_dma, mdata->base + SPI_TX_SRC_REG); + if (mdata->rx_sgl) + writel(xfer->rx_dma, mdata->base + SPI_RX_DST_REG); +} + +static int mtk_spi_fifo_transfer(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + int cnt; + struct mtk_spi *mdata = spi_master_get_devdata(master); + + mdata->cur_transfer = xfer; + mdata->xfer_len = xfer->len; + mtk_spi_prepare_transfer(master, xfer); + mtk_spi_setup_packet(master); + + if (xfer->len % 4) + cnt = xfer->len / 4 + 1; + else + cnt = xfer->len / 4; + iowrite32_rep(mdata->base + SPI_TX_DATA_REG, xfer->tx_buf, cnt); + + mtk_spi_enable_transfer(master); + + return 1; +} + +static int mtk_spi_dma_transfer(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + int cmd; + struct mtk_spi *mdata = spi_master_get_devdata(master); + + mdata->tx_sgl = NULL; + mdata->rx_sgl = NULL; + mdata->tx_sgl_len = 0; + mdata->rx_sgl_len = 0; + mdata->cur_transfer = xfer; + + mtk_spi_prepare_transfer(master, xfer); + + cmd = readl(mdata->base + SPI_CMD_REG); + if (xfer->tx_buf) + cmd |= SPI_CMD_TX_DMA; + if (xfer->rx_buf) + cmd |= SPI_CMD_RX_DMA; + writel(cmd, mdata->base + SPI_CMD_REG); + + if (xfer->tx_buf) + mdata->tx_sgl = xfer->tx_sg.sgl; + if (xfer->rx_buf) + mdata->rx_sgl = xfer->rx_sg.sgl; + + if (mdata->tx_sgl) { + xfer->tx_dma = sg_dma_address(mdata->tx_sgl); + mdata->tx_sgl_len = sg_dma_len(mdata->tx_sgl); + } + if (mdata->rx_sgl) { + xfer->rx_dma = sg_dma_address(mdata->rx_sgl); + mdata->rx_sgl_len = sg_dma_len(mdata->rx_sgl); + } + + mtk_spi_update_mdata_len(master); + mtk_spi_setup_packet(master); + mtk_spi_setup_dma_addr(master, xfer); + mtk_spi_enable_transfer(master); + + return 1; +} + +static int mtk_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + if (master->can_dma(master, spi, xfer)) + return mtk_spi_dma_transfer(master, spi, xfer); + else + return mtk_spi_fifo_transfer(master, spi, xfer); +} + +static bool mtk_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + return xfer->len > MTK_SPI_MAX_FIFO_SIZE; +} + +static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id) +{ + u32 cmd, reg_val, cnt; + struct spi_master *master = dev_id; + struct mtk_spi *mdata = spi_master_get_devdata(master); + struct spi_transfer *trans = mdata->cur_transfer; + + reg_val = readl(mdata->base + SPI_STATUS0_REG); + if (reg_val & MTK_SPI_PAUSE_INT_STATUS) + mdata->state = MTK_SPI_PAUSED; + else + mdata->state = MTK_SPI_IDLE; + + if (!master->can_dma(master, master->cur_msg->spi, trans)) { + if (trans->rx_buf) { + if (mdata->xfer_len % 4) + cnt = mdata->xfer_len / 4 + 1; + else + cnt = mdata->xfer_len / 4; + ioread32_rep(mdata->base + SPI_RX_DATA_REG, + trans->rx_buf, cnt); + } + spi_finalize_current_transfer(master); + return IRQ_HANDLED; + } + + if (mdata->tx_sgl) + trans->tx_dma += mdata->xfer_len; + if (mdata->rx_sgl) + trans->rx_dma += mdata->xfer_len; + + if (mdata->tx_sgl && (mdata->tx_sgl_len == 0)) { + mdata->tx_sgl = sg_next(mdata->tx_sgl); + if (mdata->tx_sgl) { + trans->tx_dma = sg_dma_address(mdata->tx_sgl); + mdata->tx_sgl_len = sg_dma_len(mdata->tx_sgl); + } + } + if (mdata->rx_sgl && (mdata->rx_sgl_len == 0)) { + mdata->rx_sgl = sg_next(mdata->rx_sgl); + if (mdata->rx_sgl) { + trans->rx_dma = sg_dma_address(mdata->rx_sgl); + mdata->rx_sgl_len = sg_dma_len(mdata->rx_sgl); + } + } + + if (!mdata->tx_sgl && !mdata->rx_sgl) { + /* spi disable dma */ + cmd = readl(mdata->base + SPI_CMD_REG); + cmd &= ~SPI_CMD_TX_DMA; + cmd &= ~SPI_CMD_RX_DMA; + writel(cmd, mdata->base + SPI_CMD_REG); + + spi_finalize_current_transfer(master); + return IRQ_HANDLED; + } + + mtk_spi_update_mdata_len(master); + mtk_spi_setup_packet(master); + mtk_spi_setup_dma_addr(master, trans); + mtk_spi_enable_transfer(master); + + return IRQ_HANDLED; +} + +static int mtk_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct mtk_spi *mdata; + const struct of_device_id *of_id; + struct resource *res; + int irq, ret; + + master = spi_alloc_master(&pdev->dev, sizeof(*mdata)); + if (!master) { + dev_err(&pdev->dev, "failed to alloc spi master\n"); + return -ENOMEM; + } + + master->auto_runtime_pm = true; + master->dev.of_node = pdev->dev.of_node; + master->mode_bits = SPI_CPOL | SPI_CPHA; + + master->set_cs = mtk_spi_set_cs; + master->prepare_message = mtk_spi_prepare_message; + master->transfer_one = mtk_spi_transfer_one; + master->can_dma = mtk_spi_can_dma; + + of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node); + if (!of_id) { + dev_err(&pdev->dev, "failed to probe of_node\n"); + ret = -EINVAL; + goto err_put_master; + } + + mdata = spi_master_get_devdata(master); + mdata->dev_comp = of_id->data; + if (mdata->dev_comp->must_tx) + master->flags = SPI_MASTER_MUST_TX; + + if (mdata->dev_comp->need_pad_sel) { + ret = of_property_read_u32(pdev->dev.of_node, + "mediatek,pad-select", + &mdata->pad_sel); + if (ret) { + dev_err(&pdev->dev, "failed to read pad select: %d\n", + ret); + goto err_put_master; + } + + if (mdata->pad_sel > MT8173_SPI_MAX_PAD_SEL) { + dev_err(&pdev->dev, "wrong pad-select: %u\n", + mdata->pad_sel); + ret = -EINVAL; + goto err_put_master; + } + } + + platform_set_drvdata(pdev, master); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + dev_err(&pdev->dev, "failed to determine base address\n"); + goto err_put_master; + } + + mdata->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mdata->base)) { + ret = PTR_ERR(mdata->base); + goto err_put_master; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get irq (%d)\n", irq); + ret = irq; + goto err_put_master; + } + + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + + ret = devm_request_irq(&pdev->dev, irq, mtk_spi_interrupt, + IRQF_TRIGGER_NONE, dev_name(&pdev->dev), master); + if (ret) { + dev_err(&pdev->dev, "failed to register irq (%d)\n", ret); + goto err_put_master; + } + + mdata->parent_clk = devm_clk_get(&pdev->dev, "parent-clk"); + if (IS_ERR(mdata->parent_clk)) { + ret = PTR_ERR(mdata->parent_clk); + dev_err(&pdev->dev, "failed to get parent-clk: %d\n", ret); + goto err_put_master; + } + + mdata->sel_clk = devm_clk_get(&pdev->dev, "sel-clk"); + if (IS_ERR(mdata->sel_clk)) { + ret = PTR_ERR(mdata->sel_clk); + dev_err(&pdev->dev, "failed to get sel-clk: %d\n", ret); + goto err_put_master; + } + + mdata->spi_clk = devm_clk_get(&pdev->dev, "spi-clk"); + if (IS_ERR(mdata->spi_clk)) { + ret = PTR_ERR(mdata->spi_clk); + dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret); + goto err_put_master; + } + + ret = clk_prepare_enable(mdata->spi_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret); + goto err_put_master; + } + + ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret); + goto err_disable_clk; + } + + clk_disable_unprepare(mdata->spi_clk); + + pm_runtime_enable(&pdev->dev); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&pdev->dev, "failed to register master (%d)\n", ret); + goto err_put_master; + } + + return 0; + +err_disable_clk: + clk_disable_unprepare(mdata->spi_clk); +err_put_master: + spi_master_put(master); + + return ret; +} + +static int mtk_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct mtk_spi *mdata = spi_master_get_devdata(master); + + pm_runtime_disable(&pdev->dev); + + mtk_spi_reset(mdata); + spi_master_put(master); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mtk_spi_suspend(struct device *dev) +{ + int ret; + struct spi_master *master = dev_get_drvdata(dev); + struct mtk_spi *mdata = spi_master_get_devdata(master); + + ret = spi_master_suspend(master); + if (ret) + return ret; + + if (!pm_runtime_suspended(dev)) + clk_disable_unprepare(mdata->spi_clk); + + return ret; +} + +static int mtk_spi_resume(struct device *dev) +{ + int ret; + struct spi_master *master = dev_get_drvdata(dev); + struct mtk_spi *mdata = spi_master_get_devdata(master); + + if (!pm_runtime_suspended(dev)) { + ret = clk_prepare_enable(mdata->spi_clk); + if (ret < 0) { + dev_err(dev, "failed to enable spi_clk (%d)\n", ret); + return ret; + } + } + + ret = spi_master_resume(master); + if (ret < 0) + clk_disable_unprepare(mdata->spi_clk); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int mtk_spi_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct mtk_spi *mdata = spi_master_get_devdata(master); + + clk_disable_unprepare(mdata->spi_clk); + + return 0; +} + +static int mtk_spi_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct mtk_spi *mdata = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(mdata->spi_clk); + if (ret < 0) { + dev_err(dev, "failed to enable spi_clk (%d)\n", ret); + return ret; + } + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops mtk_spi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(mtk_spi_suspend, mtk_spi_resume) + SET_RUNTIME_PM_OPS(mtk_spi_runtime_suspend, + mtk_spi_runtime_resume, NULL) +}; + +static struct platform_driver mtk_spi_driver = { + .driver = { + .name = "mtk-spi", + .pm = &mtk_spi_pm, + .of_match_table = mtk_spi_of_match, + }, + .probe = mtk_spi_probe, + .remove = mtk_spi_remove, +}; + +module_platform_driver(mtk_spi_driver); + +MODULE_DESCRIPTION("MTK SPI Controller driver"); +MODULE_AUTHOR("Leilk Liu <leilk.liu@mediatek.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtk-spi"); diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 3cfd43574..d19d7f28a 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -7,7 +7,6 @@ #include <linux/of_device.h> #include <linux/module.h> #include <linux/spi/pxa2xx_spi.h> -#include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/dmaengine.h> diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 8e4b1a7c3..a8ef38ebb 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -21,6 +21,7 @@ #include <linux/err.h> #include <linux/interrupt.h> #include <linux/kernel.h> +#include <linux/pci.h> #include <linux/platform_device.h> #include <linux/spi/pxa2xx_spi.h> #include <linux/spi/spi.h> @@ -97,6 +98,15 @@ static const struct lpss_config lpss_platforms[] = { .tx_threshold_lo = 160, .tx_threshold_hi = 224, }, + { /* LPSS_SPT_SSP */ + .offset = 0x200, + .reg_general = -1, + .reg_ssp = 0x20, + .reg_cs_ctrl = 0x24, + .rx_threshold = 1, + .tx_threshold_lo = 32, + .tx_threshold_hi = 56, + }, }; static inline const struct lpss_config @@ -110,6 +120,7 @@ static bool is_lpss_ssp(const struct driver_data *drv_data) switch (drv_data->ssp_type) { case LPSS_LPT_SSP: case LPSS_BYT_SSP: + case LPSS_SPT_SSP: return true; default: return false; @@ -1111,6 +1122,7 @@ static int setup(struct spi_device *spi) break; case LPSS_LPT_SSP: case LPSS_BYT_SSP: + case LPSS_SPT_SSP: config = lpss_get_config(drv_data); tx_thres = config->tx_threshold_lo; tx_hi_thres = config->tx_threshold_hi; @@ -1280,6 +1292,31 @@ static const struct acpi_device_id pxa2xx_spi_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); +/* + * PCI IDs of compound devices that integrate both host controller and private + * integrated DMA engine. Please note these are not used in module + * autoloading and probing in this module but matching the LPSS SSP type. + */ +static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { + /* SPT-LP */ + { PCI_VDEVICE(INTEL, 0x9d29), LPSS_SPT_SSP }, + { PCI_VDEVICE(INTEL, 0x9d2a), LPSS_SPT_SSP }, + /* SPT-H */ + { PCI_VDEVICE(INTEL, 0xa129), LPSS_SPT_SSP }, + { PCI_VDEVICE(INTEL, 0xa12a), LPSS_SPT_SSP }, + { }, +}; + +static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param) +{ + struct device *dev = param; + + if (dev != chan->device->dev->parent) + return false; + + return true; +} + static struct pxa2xx_spi_master * pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) { @@ -1287,16 +1324,25 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) struct acpi_device *adev; struct ssp_device *ssp; struct resource *res; - const struct acpi_device_id *id; + const struct acpi_device_id *adev_id = NULL; + const struct pci_device_id *pcidev_id = NULL; int devid, type; if (!ACPI_HANDLE(&pdev->dev) || acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) return NULL; - id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); - if (id) - type = (int)id->driver_data; + if (dev_is_pci(pdev->dev.parent)) + pcidev_id = pci_match_id(pxa2xx_spi_pci_compound_match, + to_pci_dev(pdev->dev.parent)); + else + adev_id = acpi_match_device(pdev->dev.driver->acpi_match_table, + &pdev->dev); + + if (adev_id) + type = (int)adev_id->driver_data; + else if (pcidev_id) + type = (int)pcidev_id->driver_data; else return NULL; @@ -1315,6 +1361,12 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) if (IS_ERR(ssp->mmio_base)) return NULL; + if (pcidev_id) { + pdata->tx_param = pdev->dev.parent; + pdata->rx_param = pdev->dev.parent; + pdata->dma_filter = pxa2xx_spi_idma_filter; + } + ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->irq = platform_get_irq(pdev, 0); ssp->type = type; @@ -1366,8 +1418,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) return -ENODEV; } - /* Allocate master with space for drv_data and null dma buffer */ - master = spi_alloc_master(dev, sizeof(struct driver_data) + 16); + master = spi_alloc_master(dev, sizeof(struct driver_data)); if (!master) { dev_err(&pdev->dev, "cannot alloc spi_master\n"); pxa_ssp_free(ssp); @@ -1394,7 +1445,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) master->auto_runtime_pm = true; drv_data->ssp_type = ssp->type; - drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT); drv_data->ioaddr = ssp->mmio_base; drv_data->ssdr_physical = ssp->phys_base + SSDR; @@ -1428,8 +1478,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) } /* Setup DMA if requested */ - drv_data->tx_channel = -1; - drv_data->rx_channel = -1; if (platform_info->enable_dma) { status = pxa2xx_spi_dma_setup(drv_data); if (status) { diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 9f01e9c9a..0a9b6390a 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -36,11 +36,6 @@ struct driver_data { /* PXA hookup */ struct pxa2xx_spi_master *master_info; - /* PXA private DMA setup stuff */ - int rx_channel; - int tx_channel; - u32 *null_dma_buf; - /* SSP register addresses */ void __iomem *ioaddr; u32 ssdr_physical; diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 68e7efeb9..79a8bc4f6 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -645,7 +645,6 @@ static int rockchip_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); rs = spi_master_get_devdata(master); - memset(rs, 0, sizeof(struct rockchip_spi)); /* Get basic io resource and map it */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index f9189a0c8..818843336 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -383,7 +383,8 @@ static void qspi_update(const struct rspi_data *rspi, u8 mask, u8 val, u8 reg) rspi_write8(rspi, data, reg); } -static int qspi_set_send_trigger(struct rspi_data *rspi, unsigned int len) +static unsigned int qspi_set_send_trigger(struct rspi_data *rspi, + unsigned int len) { unsigned int n; @@ -724,25 +725,25 @@ static int rspi_rz_transfer_one(struct spi_master *master, static int qspi_trigger_transfer_out_in(struct rspi_data *rspi, const u8 *tx, u8 *rx, unsigned int len) { - int i, n, ret; - int error; + unsigned int i, n; + int ret; while (len > 0) { n = qspi_set_send_trigger(rspi, len); qspi_set_receive_trigger(rspi, len); if (n == QSPI_BUFFER_SIZE) { - error = rspi_wait_for_tx_empty(rspi); - if (error < 0) { + ret = rspi_wait_for_tx_empty(rspi); + if (ret < 0) { dev_err(&rspi->master->dev, "transmit timeout\n"); - return error; + return ret; } for (i = 0; i < n; i++) rspi_write_data(rspi, *tx++); - error = rspi_wait_for_rx_full(rspi); - if (error < 0) { + ret = rspi_wait_for_rx_full(rspi); + if (ret < 0) { dev_err(&rspi->master->dev, "receive timeout\n"); - return error; + return ret; } for (i = 0; i < n; i++) *rx++ = rspi_read_data(rspi); diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index f747ca269..f36bc320a 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -501,7 +501,6 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) } hw = spi_master_get_devdata(master); - memset(hw, 0, sizeof(struct s3c24xx_spi)); hw->master = master; hw->pdata = pdata = dev_get_platdata(&pdev->dev); diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 2a8c513c4..cd1cfac04 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1191,8 +1191,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", sdd->port_id, master->num_chipselect); - dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tDMA=[Rx-%d, Tx-%d]\n", - mem_res, + dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%d, Tx-%d]\n", + mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1, sdd->rx_dma.dmach, sdd->tx_dma.dmach); return 0; diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index a7629f8ed..a7934ab00 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1268,11 +1268,6 @@ static int sh_msiof_spi_remove(struct platform_device *pdev) static const struct platform_device_id spi_driver_ids[] = { { "spi_sh_msiof", (kernel_ulong_t)&sh_data }, - { "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data }, - { "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data }, - { "spi_r8a7792_msiof", (kernel_ulong_t)&r8a779x_data }, - { "spi_r8a7793_msiof", (kernel_ulong_t)&r8a779x_data }, - { "spi_r8a7794_msiof", (kernel_ulong_t)&r8a779x_data }, {}, }; MODULE_DEVICE_TABLE(platform, spi_driver_ids); diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 5c0616870..aa6d28413 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -99,6 +99,8 @@ struct ti_qspi { #define QSPI_INVAL (4 << 16) #define QSPI_WC_CMD_INT_EN (1 << 14) #define QSPI_FLEN(n) ((n - 1) << 0) +#define QSPI_WLEN_MAX_BITS 128 +#define QSPI_WLEN_MAX_BYTES 16 /* STATUS REGISTER */ #define BUSY 0x01 @@ -217,14 +219,16 @@ static inline u32 qspi_is_busy(struct ti_qspi *qspi) static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) { - int wlen, count; + int wlen, count, xfer_len; unsigned int cmd; const u8 *txbuf; + u32 data; txbuf = t->tx_buf; cmd = qspi->cmd | QSPI_WR_SNGL; count = t->len; wlen = t->bits_per_word >> 3; /* in bytes */ + xfer_len = wlen; while (count) { if (qspi_is_busy(qspi)) @@ -234,7 +238,29 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) case 1: dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %02x\n", cmd, qspi->dc, *txbuf); - writeb(*txbuf, qspi->base + QSPI_SPI_DATA_REG); + if (count >= QSPI_WLEN_MAX_BYTES) { + u32 *txp = (u32 *)txbuf; + + data = cpu_to_be32(*txp++); + writel(data, qspi->base + + QSPI_SPI_DATA_REG_3); + data = cpu_to_be32(*txp++); + writel(data, qspi->base + + QSPI_SPI_DATA_REG_2); + data = cpu_to_be32(*txp++); + writel(data, qspi->base + + QSPI_SPI_DATA_REG_1); + data = cpu_to_be32(*txp++); + writel(data, qspi->base + + QSPI_SPI_DATA_REG); + xfer_len = QSPI_WLEN_MAX_BYTES; + cmd |= QSPI_WLEN(QSPI_WLEN_MAX_BITS); + } else { + writeb(*txbuf, qspi->base + QSPI_SPI_DATA_REG); + cmd = qspi->cmd | QSPI_WR_SNGL; + xfer_len = wlen; + cmd |= QSPI_WLEN(wlen); + } break; case 2: dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %04x\n", @@ -254,8 +280,8 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) dev_err(qspi->dev, "write timed out\n"); return -ETIMEDOUT; } - txbuf += wlen; - count -= wlen; + txbuf += xfer_len; + count -= xfer_len; } return 0; diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c index bb478dccf..3c28e24b1 100644 --- a/drivers/spi/spi-xcomm.c +++ b/drivers/spi/spi-xcomm.c @@ -237,11 +237,11 @@ static const struct i2c_device_id spi_xcomm_ids[] = { { "spi-xcomm" }, { }, }; +MODULE_DEVICE_TABLE(i2c, spi_xcomm_ids); static struct i2c_driver spi_xcomm_driver = { .driver = { .name = "spi-xcomm", - .owner = THIS_MODULE, }, .id_table = spi_xcomm_ids, .probe = spi_xcomm_probe, diff --git a/drivers/spi/spi-xlp.c b/drivers/spi/spi-xlp.c new file mode 100644 index 000000000..8f04feca6 --- /dev/null +++ b/drivers/spi/spi-xlp.c @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2003-2015 Broadcom Corporation + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (GPL v2) + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/of.h> +#include <linux/interrupt.h> + +/* SPI Configuration Register */ +#define XLP_SPI_CONFIG 0x00 +#define XLP_SPI_CPHA BIT(0) +#define XLP_SPI_CPOL BIT(1) +#define XLP_SPI_CS_POL BIT(2) +#define XLP_SPI_TXMISO_EN BIT(3) +#define XLP_SPI_TXMOSI_EN BIT(4) +#define XLP_SPI_RXMISO_EN BIT(5) +#define XLP_SPI_CS_LSBFE BIT(10) +#define XLP_SPI_RXCAP_EN BIT(11) + +/* SPI Frequency Divider Register */ +#define XLP_SPI_FDIV 0x04 + +/* SPI Command Register */ +#define XLP_SPI_CMD 0x08 +#define XLP_SPI_CMD_IDLE_MASK 0x0 +#define XLP_SPI_CMD_TX_MASK 0x1 +#define XLP_SPI_CMD_RX_MASK 0x2 +#define XLP_SPI_CMD_TXRX_MASK 0x3 +#define XLP_SPI_CMD_CONT BIT(4) +#define XLP_SPI_XFR_BITCNT_SHIFT 16 + +/* SPI Status Register */ +#define XLP_SPI_STATUS 0x0c +#define XLP_SPI_XFR_PENDING BIT(0) +#define XLP_SPI_XFR_DONE BIT(1) +#define XLP_SPI_TX_INT BIT(2) +#define XLP_SPI_RX_INT BIT(3) +#define XLP_SPI_TX_UF BIT(4) +#define XLP_SPI_RX_OF BIT(5) +#define XLP_SPI_STAT_MASK 0x3f + +/* SPI Interrupt Enable Register */ +#define XLP_SPI_INTR_EN 0x10 +#define XLP_SPI_INTR_DONE BIT(0) +#define XLP_SPI_INTR_TXTH BIT(1) +#define XLP_SPI_INTR_RXTH BIT(2) +#define XLP_SPI_INTR_TXUF BIT(3) +#define XLP_SPI_INTR_RXOF BIT(4) + +/* SPI FIFO Threshold Register */ +#define XLP_SPI_FIFO_THRESH 0x14 + +/* SPI FIFO Word Count Register */ +#define XLP_SPI_FIFO_WCNT 0x18 +#define XLP_SPI_RXFIFO_WCNT_MASK 0xf +#define XLP_SPI_TXFIFO_WCNT_MASK 0xf0 +#define XLP_SPI_TXFIFO_WCNT_SHIFT 4 + +/* SPI Transmit Data FIFO Register */ +#define XLP_SPI_TXDATA_FIFO 0x1c + +/* SPI Receive Data FIFO Register */ +#define XLP_SPI_RXDATA_FIFO 0x20 + +/* SPI System Control Register */ +#define XLP_SPI_SYSCTRL 0x100 +#define XLP_SPI_SYS_RESET BIT(0) +#define XLP_SPI_SYS_CLKDIS BIT(1) +#define XLP_SPI_SYS_PMEN BIT(8) + +#define SPI_CS_OFFSET 0x40 +#define XLP_SPI_TXRXTH 0x80 +#define XLP_SPI_FIFO_SIZE 8 +#define XLP_SPI_MAX_CS 4 +#define XLP_SPI_DEFAULT_FREQ 133333333 +#define XLP_SPI_FDIV_MIN 4 +#define XLP_SPI_FDIV_MAX 65535 +/* + * SPI can transfer only 28 bytes properly at a time. So split the + * transfer into 28 bytes size. + */ +#define XLP_SPI_XFER_SIZE 28 + +struct xlp_spi_priv { + struct device dev; /* device structure */ + void __iomem *base; /* spi registers base address */ + const u8 *tx_buf; /* tx data buffer */ + u8 *rx_buf; /* rx data buffer */ + int tx_len; /* tx xfer length */ + int rx_len; /* rx xfer length */ + int txerrors; /* TXFIFO underflow count */ + int rxerrors; /* RXFIFO overflow count */ + int cs; /* slave device chip select */ + u32 spi_clk; /* spi clock frequency */ + bool cmd_cont; /* cs active */ + struct completion done; /* completion notification */ +}; + +static inline u32 xlp_spi_reg_read(struct xlp_spi_priv *priv, + int cs, int regoff) +{ + return readl(priv->base + regoff + cs * SPI_CS_OFFSET); +} + +static inline void xlp_spi_reg_write(struct xlp_spi_priv *priv, int cs, + int regoff, u32 val) +{ + writel(val, priv->base + regoff + cs * SPI_CS_OFFSET); +} + +static inline void xlp_spi_sysctl_write(struct xlp_spi_priv *priv, + int regoff, u32 val) +{ + writel(val, priv->base + regoff); +} + +/* + * Setup global SPI_SYSCTRL register for all SPI channels. + */ +static void xlp_spi_sysctl_setup(struct xlp_spi_priv *xspi) +{ + int cs; + + for (cs = 0; cs < XLP_SPI_MAX_CS; cs++) + xlp_spi_sysctl_write(xspi, XLP_SPI_SYSCTRL, + XLP_SPI_SYS_RESET << cs); + xlp_spi_sysctl_write(xspi, XLP_SPI_SYSCTRL, XLP_SPI_SYS_PMEN); +} + +static int xlp_spi_setup(struct spi_device *spi) +{ + struct xlp_spi_priv *xspi; + u32 fdiv, cfg; + int cs; + + xspi = spi_master_get_devdata(spi->master); + cs = spi->chip_select; + /* + * The value of fdiv must be between 4 and 65535. + */ + fdiv = DIV_ROUND_UP(xspi->spi_clk, spi->max_speed_hz); + if (fdiv > XLP_SPI_FDIV_MAX) + fdiv = XLP_SPI_FDIV_MAX; + else if (fdiv < XLP_SPI_FDIV_MIN) + fdiv = XLP_SPI_FDIV_MIN; + + xlp_spi_reg_write(xspi, cs, XLP_SPI_FDIV, fdiv); + xlp_spi_reg_write(xspi, cs, XLP_SPI_FIFO_THRESH, XLP_SPI_TXRXTH); + cfg = xlp_spi_reg_read(xspi, cs, XLP_SPI_CONFIG); + if (spi->mode & SPI_CPHA) + cfg |= XLP_SPI_CPHA; + else + cfg &= ~XLP_SPI_CPHA; + if (spi->mode & SPI_CPOL) + cfg |= XLP_SPI_CPOL; + else + cfg &= ~XLP_SPI_CPOL; + if (!(spi->mode & SPI_CS_HIGH)) + cfg |= XLP_SPI_CS_POL; + else + cfg &= ~XLP_SPI_CS_POL; + if (spi->mode & SPI_LSB_FIRST) + cfg |= XLP_SPI_CS_LSBFE; + else + cfg &= ~XLP_SPI_CS_LSBFE; + + cfg |= XLP_SPI_TXMOSI_EN | XLP_SPI_RXMISO_EN; + if (fdiv == 4) + cfg |= XLP_SPI_RXCAP_EN; + xlp_spi_reg_write(xspi, cs, XLP_SPI_CONFIG, cfg); + + return 0; +} + +static void xlp_spi_read_rxfifo(struct xlp_spi_priv *xspi) +{ + u32 rx_data, rxfifo_cnt; + int i, j, nbytes; + + rxfifo_cnt = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_FIFO_WCNT); + rxfifo_cnt &= XLP_SPI_RXFIFO_WCNT_MASK; + while (rxfifo_cnt) { + rx_data = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_RXDATA_FIFO); + j = 0; + nbytes = min(xspi->rx_len, 4); + for (i = nbytes - 1; i >= 0; i--, j++) + xspi->rx_buf[i] = (rx_data >> (j * 8)) & 0xff; + + xspi->rx_len -= nbytes; + xspi->rx_buf += nbytes; + rxfifo_cnt--; + } +} + +static void xlp_spi_fill_txfifo(struct xlp_spi_priv *xspi) +{ + u32 tx_data, txfifo_cnt; + int i, j, nbytes; + + txfifo_cnt = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_FIFO_WCNT); + txfifo_cnt &= XLP_SPI_TXFIFO_WCNT_MASK; + txfifo_cnt >>= XLP_SPI_TXFIFO_WCNT_SHIFT; + while (xspi->tx_len && (txfifo_cnt < XLP_SPI_FIFO_SIZE)) { + j = 0; + tx_data = 0; + nbytes = min(xspi->tx_len, 4); + for (i = nbytes - 1; i >= 0; i--, j++) + tx_data |= xspi->tx_buf[i] << (j * 8); + + xlp_spi_reg_write(xspi, xspi->cs, XLP_SPI_TXDATA_FIFO, tx_data); + xspi->tx_len -= nbytes; + xspi->tx_buf += nbytes; + txfifo_cnt++; + } +} + +static irqreturn_t xlp_spi_interrupt(int irq, void *dev_id) +{ + struct xlp_spi_priv *xspi = dev_id; + u32 stat; + + stat = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_STATUS) & + XLP_SPI_STAT_MASK; + if (!stat) + return IRQ_NONE; + + if (stat & XLP_SPI_TX_INT) { + if (xspi->tx_len) + xlp_spi_fill_txfifo(xspi); + if (stat & XLP_SPI_TX_UF) + xspi->txerrors++; + } + + if (stat & XLP_SPI_RX_INT) { + if (xspi->rx_len) + xlp_spi_read_rxfifo(xspi); + if (stat & XLP_SPI_RX_OF) + xspi->rxerrors++; + } + + /* write status back to clear interrupts */ + xlp_spi_reg_write(xspi, xspi->cs, XLP_SPI_STATUS, stat); + if (stat & XLP_SPI_XFR_DONE) + complete(&xspi->done); + + return IRQ_HANDLED; +} + +static void xlp_spi_send_cmd(struct xlp_spi_priv *xspi, int xfer_len, + int cmd_cont) +{ + u32 cmd = 0; + + if (xspi->tx_buf) + cmd |= XLP_SPI_CMD_TX_MASK; + if (xspi->rx_buf) + cmd |= XLP_SPI_CMD_RX_MASK; + if (cmd_cont) + cmd |= XLP_SPI_CMD_CONT; + cmd |= ((xfer_len * 8 - 1) << XLP_SPI_XFR_BITCNT_SHIFT); + xlp_spi_reg_write(xspi, xspi->cs, XLP_SPI_CMD, cmd); +} + +static int xlp_spi_xfer_block(struct xlp_spi_priv *xs, + const unsigned char *tx_buf, + unsigned char *rx_buf, int xfer_len, int cmd_cont) +{ + int timeout; + u32 intr_mask = 0; + + xs->tx_buf = tx_buf; + xs->rx_buf = rx_buf; + xs->tx_len = (xs->tx_buf == NULL) ? 0 : xfer_len; + xs->rx_len = (xs->rx_buf == NULL) ? 0 : xfer_len; + xs->txerrors = xs->rxerrors = 0; + + /* fill TXDATA_FIFO, then send the CMD */ + if (xs->tx_len) + xlp_spi_fill_txfifo(xs); + + xlp_spi_send_cmd(xs, xfer_len, cmd_cont); + + /* + * We are getting some spurious tx interrupts, so avoid enabling + * tx interrupts when only rx is in process. + * Enable all the interrupts in tx case. + */ + if (xs->tx_len) + intr_mask |= XLP_SPI_INTR_TXTH | XLP_SPI_INTR_TXUF | + XLP_SPI_INTR_RXTH | XLP_SPI_INTR_RXOF; + else + intr_mask |= XLP_SPI_INTR_RXTH | XLP_SPI_INTR_RXOF; + + intr_mask |= XLP_SPI_INTR_DONE; + xlp_spi_reg_write(xs, xs->cs, XLP_SPI_INTR_EN, intr_mask); + + timeout = wait_for_completion_timeout(&xs->done, + msecs_to_jiffies(1000)); + /* Disable interrupts */ + xlp_spi_reg_write(xs, xs->cs, XLP_SPI_INTR_EN, 0x0); + if (!timeout) { + dev_err(&xs->dev, "xfer timedout!\n"); + goto out; + } + if (xs->txerrors || xs->rxerrors) + dev_err(&xs->dev, "Over/Underflow rx %d tx %d xfer %d!\n", + xs->rxerrors, xs->txerrors, xfer_len); + + return xfer_len; +out: + return -ETIMEDOUT; +} + +static int xlp_spi_txrx_bufs(struct xlp_spi_priv *xs, struct spi_transfer *t) +{ + int bytesleft, sz; + unsigned char *rx_buf; + const unsigned char *tx_buf; + + tx_buf = t->tx_buf; + rx_buf = t->rx_buf; + bytesleft = t->len; + while (bytesleft) { + if (bytesleft > XLP_SPI_XFER_SIZE) + sz = xlp_spi_xfer_block(xs, tx_buf, rx_buf, + XLP_SPI_XFER_SIZE, 1); + else + sz = xlp_spi_xfer_block(xs, tx_buf, rx_buf, + bytesleft, xs->cmd_cont); + if (sz < 0) + return sz; + bytesleft -= sz; + if (tx_buf) + tx_buf += sz; + if (rx_buf) + rx_buf += sz; + } + return bytesleft; +} + +static int xlp_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct xlp_spi_priv *xspi = spi_master_get_devdata(master); + int ret = 0; + + xspi->cs = spi->chip_select; + xspi->dev = spi->dev; + + if (spi_transfer_is_last(master, t)) + xspi->cmd_cont = 0; + else + xspi->cmd_cont = 1; + + if (xlp_spi_txrx_bufs(xspi, t)) + ret = -EIO; + + spi_finalize_current_transfer(master); + return ret; +} + +static int xlp_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct xlp_spi_priv *xspi; + struct resource *res; + struct clk *clk; + int irq, err; + + xspi = devm_kzalloc(&pdev->dev, sizeof(*xspi), GFP_KERNEL); + if (!xspi) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xspi->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xspi->base)) + return PTR_ERR(xspi->base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ resource found\n"); + return -EINVAL; + } + err = devm_request_irq(&pdev->dev, irq, xlp_spi_interrupt, 0, + pdev->name, xspi); + if (err) { + dev_err(&pdev->dev, "unable to request irq %d\n", irq); + return err; + } + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "could not get spi clock\n"); + return -ENODEV; + } + xspi->spi_clk = clk_get_rate(clk); + + master = spi_alloc_master(&pdev->dev, 0); + if (!master) { + dev_err(&pdev->dev, "could not alloc master\n"); + return -ENOMEM; + } + + master->bus_num = 0; + master->num_chipselect = XLP_SPI_MAX_CS; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->setup = xlp_spi_setup; + master->transfer_one = xlp_spi_transfer_one; + master->dev.of_node = pdev->dev.of_node; + + init_completion(&xspi->done); + spi_master_set_devdata(master, xspi); + xlp_spi_sysctl_setup(xspi); + + /* register spi controller */ + err = devm_spi_register_master(&pdev->dev, master); + if (err) { + dev_err(&pdev->dev, "spi register master failed!\n"); + spi_master_put(master); + return err; + } + + return 0; +} + +static const struct of_device_id xlp_spi_dt_id[] = { + { .compatible = "netlogic,xlp832-spi" }, + { }, +}; + +static struct platform_driver xlp_spi_driver = { + .probe = xlp_spi_probe, + .driver = { + .name = "xlp-spi", + .of_match_table = xlp_spi_dt_id, + }, +}; +module_platform_driver(xlp_spi_driver); + +MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>"); +MODULE_DESCRIPTION("Netlogic XLP SPI controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 9ce2f156d..a5f53de81 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -67,11 +67,141 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf) } static DEVICE_ATTR_RO(modalias); +#define SPI_STATISTICS_ATTRS(field, file) \ +static ssize_t spi_master_##field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct spi_master *master = container_of(dev, \ + struct spi_master, dev); \ + return spi_statistics_##field##_show(&master->statistics, buf); \ +} \ +static struct device_attribute dev_attr_spi_master_##field = { \ + .attr = { .name = file, .mode = S_IRUGO }, \ + .show = spi_master_##field##_show, \ +}; \ +static ssize_t spi_device_##field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct spi_device *spi = container_of(dev, \ + struct spi_device, dev); \ + return spi_statistics_##field##_show(&spi->statistics, buf); \ +} \ +static struct device_attribute dev_attr_spi_device_##field = { \ + .attr = { .name = file, .mode = S_IRUGO }, \ + .show = spi_device_##field##_show, \ +} + +#define SPI_STATISTICS_SHOW_NAME(name, file, field, format_string) \ +static ssize_t spi_statistics_##name##_show(struct spi_statistics *stat, \ + char *buf) \ +{ \ + unsigned long flags; \ + ssize_t len; \ + spin_lock_irqsave(&stat->lock, flags); \ + len = sprintf(buf, format_string, stat->field); \ + spin_unlock_irqrestore(&stat->lock, flags); \ + return len; \ +} \ +SPI_STATISTICS_ATTRS(name, file) + +#define SPI_STATISTICS_SHOW(field, format_string) \ + SPI_STATISTICS_SHOW_NAME(field, __stringify(field), \ + field, format_string) + +SPI_STATISTICS_SHOW(messages, "%lu"); +SPI_STATISTICS_SHOW(transfers, "%lu"); +SPI_STATISTICS_SHOW(errors, "%lu"); +SPI_STATISTICS_SHOW(timedout, "%lu"); + +SPI_STATISTICS_SHOW(spi_sync, "%lu"); +SPI_STATISTICS_SHOW(spi_sync_immediate, "%lu"); +SPI_STATISTICS_SHOW(spi_async, "%lu"); + +SPI_STATISTICS_SHOW(bytes, "%llu"); +SPI_STATISTICS_SHOW(bytes_rx, "%llu"); +SPI_STATISTICS_SHOW(bytes_tx, "%llu"); + static struct attribute *spi_dev_attrs[] = { &dev_attr_modalias.attr, NULL, }; -ATTRIBUTE_GROUPS(spi_dev); + +static const struct attribute_group spi_dev_group = { + .attrs = spi_dev_attrs, +}; + +static struct attribute *spi_device_statistics_attrs[] = { + &dev_attr_spi_device_messages.attr, + &dev_attr_spi_device_transfers.attr, + &dev_attr_spi_device_errors.attr, + &dev_attr_spi_device_timedout.attr, + &dev_attr_spi_device_spi_sync.attr, + &dev_attr_spi_device_spi_sync_immediate.attr, + &dev_attr_spi_device_spi_async.attr, + &dev_attr_spi_device_bytes.attr, + &dev_attr_spi_device_bytes_rx.attr, + &dev_attr_spi_device_bytes_tx.attr, + NULL, +}; + +static const struct attribute_group spi_device_statistics_group = { + .name = "statistics", + .attrs = spi_device_statistics_attrs, +}; + +static const struct attribute_group *spi_dev_groups[] = { + &spi_dev_group, + &spi_device_statistics_group, + NULL, +}; + +static struct attribute *spi_master_statistics_attrs[] = { + &dev_attr_spi_master_messages.attr, + &dev_attr_spi_master_transfers.attr, + &dev_attr_spi_master_errors.attr, + &dev_attr_spi_master_timedout.attr, + &dev_attr_spi_master_spi_sync.attr, + &dev_attr_spi_master_spi_sync_immediate.attr, + &dev_attr_spi_master_spi_async.attr, + &dev_attr_spi_master_bytes.attr, + &dev_attr_spi_master_bytes_rx.attr, + &dev_attr_spi_master_bytes_tx.attr, + NULL, +}; + +static const struct attribute_group spi_master_statistics_group = { + .name = "statistics", + .attrs = spi_master_statistics_attrs, +}; + +static const struct attribute_group *spi_master_groups[] = { + &spi_master_statistics_group, + NULL, +}; + +void spi_statistics_add_transfer_stats(struct spi_statistics *stats, + struct spi_transfer *xfer, + struct spi_master *master) +{ + unsigned long flags; + + spin_lock_irqsave(&stats->lock, flags); + + stats->transfers++; + + stats->bytes += xfer->len; + if ((xfer->tx_buf) && + (xfer->tx_buf != master->dummy_tx)) + stats->bytes_tx += xfer->len; + if ((xfer->rx_buf) && + (xfer->rx_buf != master->dummy_rx)) + stats->bytes_rx += xfer->len; + + spin_unlock_irqrestore(&stats->lock, flags); +} +EXPORT_SYMBOL_GPL(spi_statistics_add_transfer_stats); /* modalias support makes "modprobe $MODALIAS" new-style hotplug work, * and the sysfs version makes coldplug work too. @@ -249,6 +379,9 @@ struct spi_device *spi_alloc_device(struct spi_master *master) spi->dev.bus = &spi_bus_type; spi->dev.release = spidev_release; spi->cs_gpio = -ENOENT; + + spin_lock_init(&spi->statistics.lock); + device_initialize(&spi->dev); return spi; } @@ -476,21 +609,30 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, enum dma_data_direction dir) { const bool vmalloced_buf = is_vmalloc_addr(buf); - const int desc_len = vmalloced_buf ? PAGE_SIZE : master->max_dma_len; - const int sgs = DIV_ROUND_UP(len, desc_len); + int desc_len; + int sgs; struct page *vm_page; void *sg_buf; size_t min; int i, ret; + if (vmalloced_buf) { + desc_len = PAGE_SIZE; + sgs = DIV_ROUND_UP(len + offset_in_page(buf), desc_len); + } else { + desc_len = master->max_dma_len; + sgs = DIV_ROUND_UP(len, desc_len); + } + ret = sg_alloc_table(sgt, sgs, GFP_KERNEL); if (ret != 0) return ret; for (i = 0; i < sgs; i++) { - min = min_t(size_t, len, desc_len); if (vmalloced_buf) { + min = min_t(size_t, + len, desc_len - offset_in_page(buf)); vm_page = vmalloc_to_page(buf); if (!vm_page) { sg_free_table(sgt); @@ -499,6 +641,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, sg_set_page(&sgt->sgl[i], vm_page, min, offset_in_page(buf)); } else { + min = min_t(size_t, len, desc_len); sg_buf = buf; sg_set_buf(&sgt->sgl[i], sg_buf, min); } @@ -539,8 +682,15 @@ static int __spi_map_msg(struct spi_master *master, struct spi_message *msg) if (!master->can_dma) return 0; - tx_dev = master->dma_tx->device->dev; - rx_dev = master->dma_rx->device->dev; + if (master->dma_tx) + tx_dev = master->dma_tx->device->dev; + else + tx_dev = &master->dev; + + if (master->dma_rx) + rx_dev = master->dma_rx->device->dev; + else + rx_dev = &master->dev; list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!master->can_dma(master, msg->spi, xfer)) @@ -579,8 +729,15 @@ static int __spi_unmap_msg(struct spi_master *master, struct spi_message *msg) if (!master->cur_msg_mapped || !master->can_dma) return 0; - tx_dev = master->dma_tx->device->dev; - rx_dev = master->dma_rx->device->dev; + if (master->dma_tx) + tx_dev = master->dma_tx->device->dev; + else + tx_dev = &master->dev; + + if (master->dma_rx) + rx_dev = master->dma_rx->device->dev; + else + rx_dev = &master->dev; list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!master->can_dma(master, msg->spi, xfer)) @@ -689,17 +846,29 @@ static int spi_transfer_one_message(struct spi_master *master, bool keep_cs = false; int ret = 0; unsigned long ms = 1; + struct spi_statistics *statm = &master->statistics; + struct spi_statistics *stats = &msg->spi->statistics; spi_set_cs(msg->spi, true); + SPI_STATISTICS_INCREMENT_FIELD(statm, messages); + SPI_STATISTICS_INCREMENT_FIELD(stats, messages); + list_for_each_entry(xfer, &msg->transfers, transfer_list) { trace_spi_transfer_start(msg, xfer); + spi_statistics_add_transfer_stats(statm, xfer, master); + spi_statistics_add_transfer_stats(stats, xfer, master); + if (xfer->tx_buf || xfer->rx_buf) { reinit_completion(&master->xfer_completion); ret = master->transfer_one(master, msg->spi, xfer); if (ret < 0) { + SPI_STATISTICS_INCREMENT_FIELD(statm, + errors); + SPI_STATISTICS_INCREMENT_FIELD(stats, + errors); dev_err(&msg->spi->dev, "SPI transfer failed: %d\n", ret); goto out; @@ -715,6 +884,10 @@ static int spi_transfer_one_message(struct spi_master *master, } if (ms == 0) { + SPI_STATISTICS_INCREMENT_FIELD(statm, + timedout); + SPI_STATISTICS_INCREMENT_FIELD(stats, + timedout); dev_err(&msg->spi->dev, "SPI transfer timed out\n"); msg->status = -ETIMEDOUT; @@ -1416,10 +1589,10 @@ static struct class spi_master_class = { .name = "spi_master", .owner = THIS_MODULE, .dev_release = spi_master_release, + .dev_groups = spi_master_groups, }; - /** * spi_alloc_master - allocate SPI master controller * @dev: the controller, possibly using the platform_bus @@ -1584,6 +1757,8 @@ int spi_register_master(struct spi_master *master) goto done; } } + /* add statistics */ + spin_lock_init(&master->statistics.lock); mutex_lock(&board_lock); list_add_tail(&master->list, &spi_master_list); @@ -1739,6 +1914,20 @@ EXPORT_SYMBOL_GPL(spi_busnum_to_master); * other core methods are currently defined as inline functions. */ +static int __spi_validate_bits_per_word(struct spi_master *master, u8 bits_per_word) +{ + if (master->bits_per_word_mask) { + /* Only 32 bits fit in the mask */ + if (bits_per_word > 32) + return -EINVAL; + if (!(master->bits_per_word_mask & + SPI_BPW_MASK(bits_per_word))) + return -EINVAL; + } + + return 0; +} + /** * spi_setup - setup SPI mode and clock rate * @spi: the device whose settings are being modified @@ -1797,6 +1986,9 @@ int spi_setup(struct spi_device *spi) if (!spi->bits_per_word) spi->bits_per_word = 8; + if (__spi_validate_bits_per_word(spi->master, spi->bits_per_word)) + return -EINVAL; + if (!spi->max_speed_hz) spi->max_speed_hz = spi->master->max_speed_hz; @@ -1859,19 +2051,15 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) if (!xfer->speed_hz) xfer->speed_hz = spi->max_speed_hz; + if (!xfer->speed_hz) + xfer->speed_hz = master->max_speed_hz; if (master->max_speed_hz && xfer->speed_hz > master->max_speed_hz) xfer->speed_hz = master->max_speed_hz; - if (master->bits_per_word_mask) { - /* Only 32 bits fit in the mask */ - if (xfer->bits_per_word > 32) - return -EINVAL; - if (!(master->bits_per_word_mask & - BIT(xfer->bits_per_word - 1))) - return -EINVAL; - } + if (__spi_validate_bits_per_word(master, xfer->bits_per_word)) + return -EINVAL; /* * SPI transfer length should be multiple of SPI word size @@ -1938,6 +2126,9 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) message->spi = spi; + SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_async); + SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_async); + trace_spi_message_submit(message); return master->transfer(spi, message); @@ -2074,6 +2265,9 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, message->context = &done; message->spi = spi; + SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync); + SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync); + if (!bus_locked) mutex_lock(&master->bus_lock_mutex); @@ -2101,8 +2295,13 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, /* Push out the messages in the calling context if we * can. */ - if (master->transfer == spi_queued_transfer) + if (master->transfer == spi_queued_transfer) { + SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, + spi_sync_immediate); + SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, + spi_sync_immediate); __spi_pump_messages(master, false); + } wait_for_completion(&done); status = message->status; diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 97aad8f91..ef008e52f 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -602,11 +602,11 @@ static int spidev_open(struct inode *inode, struct file *filp) if (!spidev->tx_buffer) { spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL); if (!spidev->tx_buffer) { - dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); - status = -ENOMEM; + dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); + status = -ENOMEM; goto err_find_dev; - } } + } if (!spidev->rx_buffer) { spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL); @@ -710,7 +710,7 @@ static int spidev_probe(struct spi_device *spi) /* * spidev should never be referenced in DT without a specific - * compatbile string, it is a Linux implementation thing + * compatible string, it is a Linux implementation thing * rather than a description of the hardware. */ if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { |