aboutsummaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig24
-rw-r--r--drivers/spi/Makefile3
-rw-r--r--drivers/spi/spi-omap-100k.c490
-rw-r--r--drivers/spi/spi-omap-uwire.c16
-rw-r--r--drivers/spi/spi-s3c24xx-regs.h41
-rw-r--r--drivers/spi/spi-s3c24xx.c596
6 files changed, 1 insertions, 1169 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 3b1c0878bb85..2aba88a57a77 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -708,12 +708,6 @@ config SPI_TI_QSPI
This device supports single, dual and quad read support, while
it only supports single write mode.
-config SPI_OMAP_100K
- tristate "OMAP SPI 100K"
- depends on ARCH_OMAP850 || ARCH_OMAP730 || COMPILE_TEST
- help
- OMAP SPI 100K master controller for omap7xx boards.
-
config SPI_ORION
tristate "Orion SPI master"
depends on PLAT_ORION || ARCH_MVEBU || COMPILE_TEST
@@ -844,24 +838,6 @@ config SPI_QCOM_GENI
This driver can also be built as a module. If so, the module
will be called spi-geni-qcom.
-config SPI_S3C24XX
- tristate "Samsung S3C24XX series SPI"
- depends on ARCH_S3C24XX
- select SPI_BITBANG
- help
- SPI driver for Samsung S3C24XX series ARM SoCs
-
-config SPI_S3C24XX_FIQ
- bool "S3C24XX driver with FIQ pseudo-DMA"
- depends on SPI_S3C24XX
- select FIQ
- help
- Enable FIQ support for the S3C24XX SPI driver to provide pseudo
- DMA by using the fast-interrupt request framework, This allows
- the driver to get DMA-like performance when there are either
- no free DMA channels, or when doing transfers that required both
- TX and RX data paths.
-
config SPI_S3C64XX
tristate "Samsung S3C64XX/Exynos SoC series type SPI"
depends on (PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST)
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index be9ba40ef8d0..12648f75a919 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -91,7 +91,6 @@ obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
spi-octeon-objs := spi-cavium.o spi-cavium-octeon.o
obj-$(CONFIG_SPI_OCTEON) += spi-octeon.o
obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o
-obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o
obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o
obj-$(CONFIG_SPI_TI_QSPI) += spi-ti-qspi.o
obj-$(CONFIG_SPI_ORION) += spi-orion.o
@@ -112,8 +111,6 @@ obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
-obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
-spi-s3c24xx-hw-y := spi-s3c24xx.o
obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o
obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o
obj-$(CONFIG_SPI_SH) += spi-sh.o
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c
deleted file mode 100644
index 061f7394e5b9..000000000000
--- a/drivers/spi/spi-omap-100k.c
+++ /dev/null
@@ -1,490 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * OMAP7xx SPI 100k controller driver
- * Author: Fabrice Crohas <fcrohas@gmail.com>
- * from original omap1_mcspi driver
- *
- * Copyright (C) 2005, 2006 Nokia Corporation
- * Author: Samuel Ortiz <samuel.ortiz@nokia.com> and
- * Juha Yrjola <juha.yrjola@nokia.com>
- */
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-
-#include <linux/spi/spi.h>
-
-#define OMAP1_SPI100K_MAX_FREQ 48000000
-
-#define ICR_SPITAS (OMAP7XX_ICR_BASE + 0x12)
-
-#define SPI_SETUP1 0x00
-#define SPI_SETUP2 0x02
-#define SPI_CTRL 0x04
-#define SPI_STATUS 0x06
-#define SPI_TX_LSB 0x08
-#define SPI_TX_MSB 0x0a
-#define SPI_RX_LSB 0x0c
-#define SPI_RX_MSB 0x0e
-
-#define SPI_SETUP1_INT_READ_ENABLE (1UL << 5)
-#define SPI_SETUP1_INT_WRITE_ENABLE (1UL << 4)
-#define SPI_SETUP1_CLOCK_DIVISOR(x) ((x) << 1)
-#define SPI_SETUP1_CLOCK_ENABLE (1UL << 0)
-
-#define SPI_SETUP2_ACTIVE_EDGE_FALLING (0UL << 0)
-#define SPI_SETUP2_ACTIVE_EDGE_RISING (1UL << 0)
-#define SPI_SETUP2_NEGATIVE_LEVEL (0UL << 5)
-#define SPI_SETUP2_POSITIVE_LEVEL (1UL << 5)
-#define SPI_SETUP2_LEVEL_TRIGGER (0UL << 10)
-#define SPI_SETUP2_EDGE_TRIGGER (1UL << 10)
-
-#define SPI_CTRL_SEN(x) ((x) << 7)
-#define SPI_CTRL_WORD_SIZE(x) (((x) - 1) << 2)
-#define SPI_CTRL_WR (1UL << 1)
-#define SPI_CTRL_RD (1UL << 0)
-
-#define SPI_STATUS_WE (1UL << 1)
-#define SPI_STATUS_RD (1UL << 0)
-
-/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
- * cache operations; better heuristics consider wordsize and bitrate.
- */
-#define DMA_MIN_BYTES 8
-
-#define SPI_RUNNING 0
-#define SPI_SHUTDOWN 1
-
-struct omap1_spi100k {
- struct clk *ick;
- struct clk *fck;
-
- /* Virtual base address of the controller */
- void __iomem *base;
-};
-
-struct omap1_spi100k_cs {
- void __iomem *base;
- int word_len;
-};
-
-static void spi100k_enable_clock(struct spi_master *master)
-{
- unsigned int val;
- struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
-
- /* enable SPI */
- val = readw(spi100k->base + SPI_SETUP1);
- val |= SPI_SETUP1_CLOCK_ENABLE;
- writew(val, spi100k->base + SPI_SETUP1);
-}
-
-static void spi100k_disable_clock(struct spi_master *master)
-{
- unsigned int val;
- struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
-
- /* disable SPI */
- val = readw(spi100k->base + SPI_SETUP1);
- val &= ~SPI_SETUP1_CLOCK_ENABLE;
- writew(val, spi100k->base + SPI_SETUP1);
-}
-
-static void spi100k_write_data(struct spi_master *master, int len, int data)
-{
- struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
-
- /* write 16-bit word, shifting 8-bit data if necessary */
- if (len <= 8) {
- data <<= 8;
- len = 16;
- }
-
- spi100k_enable_clock(master);
- writew(data, spi100k->base + SPI_TX_MSB);
-
- writew(SPI_CTRL_SEN(0) |
- SPI_CTRL_WORD_SIZE(len) |
- SPI_CTRL_WR,
- spi100k->base + SPI_CTRL);
-
- /* Wait for bit ack send change */
- while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE)
- ;
- udelay(1000);
-
- spi100k_disable_clock(master);
-}
-
-static int spi100k_read_data(struct spi_master *master, int len)
-{
- int dataL;
- struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
-
- /* Always do at least 16 bits */
- if (len <= 8)
- len = 16;
-
- spi100k_enable_clock(master);
- writew(SPI_CTRL_SEN(0) |
- SPI_CTRL_WORD_SIZE(len) |
- SPI_CTRL_RD,
- spi100k->base + SPI_CTRL);
-
- while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD)
- ;
- udelay(1000);
-
- dataL = readw(spi100k->base + SPI_RX_LSB);
- readw(spi100k->base + SPI_RX_MSB);
- spi100k_disable_clock(master);
-
- return dataL;
-}
-
-static void spi100k_open(struct spi_master *master)
-{
- /* get control of SPI */
- struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
-
- writew(SPI_SETUP1_INT_READ_ENABLE |
- SPI_SETUP1_INT_WRITE_ENABLE |
- SPI_SETUP1_CLOCK_DIVISOR(0), spi100k->base + SPI_SETUP1);
-
- /* configure clock and interrupts */
- writew(SPI_SETUP2_ACTIVE_EDGE_FALLING |
- SPI_SETUP2_NEGATIVE_LEVEL |
- SPI_SETUP2_LEVEL_TRIGGER, spi100k->base + SPI_SETUP2);
-}
-
-static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable)
-{
- if (enable)
- writew(0x05fc, spi100k->base + SPI_CTRL);
- else
- writew(0x05fd, spi100k->base + SPI_CTRL);
-}
-
-static unsigned
-omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
-{
- struct omap1_spi100k_cs *cs = spi->controller_state;
- unsigned int count, c;
- int word_len;
-
- count = xfer->len;
- c = count;
- word_len = cs->word_len;
-
- if (word_len <= 8) {
- u8 *rx;
- const u8 *tx;
-
- rx = xfer->rx_buf;
- tx = xfer->tx_buf;
- do {
- c -= 1;
- if (xfer->tx_buf != NULL)
- spi100k_write_data(spi->master, word_len, *tx++);
- if (xfer->rx_buf != NULL)
- *rx++ = spi100k_read_data(spi->master, word_len);
- } while (c);
- } else if (word_len <= 16) {
- u16 *rx;
- const u16 *tx;
-
- rx = xfer->rx_buf;
- tx = xfer->tx_buf;
- do {
- c -= 2;
- if (xfer->tx_buf != NULL)
- spi100k_write_data(spi->master, word_len, *tx++);
- if (xfer->rx_buf != NULL)
- *rx++ = spi100k_read_data(spi->master, word_len);
- } while (c);
- } else if (word_len <= 32) {
- u32 *rx;
- const u32 *tx;
-
- rx = xfer->rx_buf;
- tx = xfer->tx_buf;
- do {
- c -= 4;
- if (xfer->tx_buf != NULL)
- spi100k_write_data(spi->master, word_len, *tx);
- if (xfer->rx_buf != NULL)
- *rx = spi100k_read_data(spi->master, word_len);
- } while (c);
- }
- return count - c;
-}
-
-/* called only when no transfer is active to this device */
-static int omap1_spi100k_setup_transfer(struct spi_device *spi,
- struct spi_transfer *t)
-{
- struct omap1_spi100k *spi100k = spi_master_get_devdata(spi->master);
- struct omap1_spi100k_cs *cs = spi->controller_state;
- u8 word_len;
-
- if (t != NULL)
- word_len = t->bits_per_word;
- else
- word_len = spi->bits_per_word;
-
- if (word_len > 32)
- return -EINVAL;
- cs->word_len = word_len;
-
- /* SPI init before transfer */
- writew(0x3e, spi100k->base + SPI_SETUP1);
- writew(0x00, spi100k->base + SPI_STATUS);
- writew(0x3e, spi100k->base + SPI_CTRL);
-
- return 0;
-}
-
-/* the spi->mode bits understood by this driver: */
-#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
-
-static int omap1_spi100k_setup(struct spi_device *spi)
-{
- int ret;
- struct omap1_spi100k *spi100k;
- struct omap1_spi100k_cs *cs = spi->controller_state;
-
- spi100k = spi_master_get_devdata(spi->master);
-
- if (!cs) {
- cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL);
- if (!cs)
- return -ENOMEM;
- cs->base = spi100k->base + spi->chip_select * 0x14;
- spi->controller_state = cs;
- }
-
- spi100k_open(spi->master);
-
- clk_prepare_enable(spi100k->ick);
- clk_prepare_enable(spi100k->fck);
-
- ret = omap1_spi100k_setup_transfer(spi, NULL);
-
- clk_disable_unprepare(spi100k->ick);
- clk_disable_unprepare(spi100k->fck);
-
- return ret;
-}
-
-static int omap1_spi100k_transfer_one_message(struct spi_master *master,
- struct spi_message *m)
-{
- struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
- struct spi_device *spi = m->spi;
- struct spi_transfer *t = NULL;
- int cs_active = 0;
- int status = 0;
-
- list_for_each_entry(t, &m->transfers, transfer_list) {
- if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
- break;
- }
- status = omap1_spi100k_setup_transfer(spi, t);
- if (status < 0)
- break;
-
- if (!cs_active) {
- omap1_spi100k_force_cs(spi100k, 1);
- cs_active = 1;
- }
-
- if (t->len) {
- unsigned count;
-
- count = omap1_spi100k_txrx_pio(spi, t);
- m->actual_length += count;
-
- if (count != t->len) {
- break;
- }
- }
-
- spi_transfer_delay_exec(t);
-
- /* ignore the "leave it on after last xfer" hint */
-
- if (t->cs_change) {
- omap1_spi100k_force_cs(spi100k, 0);
- cs_active = 0;
- }
- }
-
- status = omap1_spi100k_setup_transfer(spi, NULL);
-
- if (cs_active)
- omap1_spi100k_force_cs(spi100k, 0);
-
- m->status = status;
-
- spi_finalize_current_message(master);
-
- return status;
-}
-
-static int omap1_spi100k_probe(struct platform_device *pdev)
-{
- struct spi_master *master;
- struct omap1_spi100k *spi100k;
- int status = 0;
-
- if (!pdev->id)
- return -EINVAL;
-
- master = spi_alloc_master(&pdev->dev, sizeof(*spi100k));
- if (master == NULL) {
- dev_dbg(&pdev->dev, "master allocation failed\n");
- return -ENOMEM;
- }
-
- if (pdev->id != -1)
- master->bus_num = pdev->id;
-
- master->setup = omap1_spi100k_setup;
- master->transfer_one_message = omap1_spi100k_transfer_one_message;
- master->num_chipselect = 2;
- master->mode_bits = MODEBITS;
- master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
- master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16);
- master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ;
- master->auto_runtime_pm = true;
-
- spi100k = spi_master_get_devdata(master);
-
- /*
- * The memory region base address is taken as the platform_data.
- * You should allocate this with ioremap() before initializing
- * the SPI.
- */
- spi100k->base = (void __iomem *)dev_get_platdata(&pdev->dev);
-
- spi100k->ick = devm_clk_get(&pdev->dev, "ick");
- if (IS_ERR(spi100k->ick)) {
- dev_dbg(&pdev->dev, "can't get spi100k_ick\n");
- status = PTR_ERR(spi100k->ick);
- goto err;
- }
-
- spi100k->fck = devm_clk_get(&pdev->dev, "fck");
- if (IS_ERR(spi100k->fck)) {
- dev_dbg(&pdev->dev, "can't get spi100k_fck\n");
- status = PTR_ERR(spi100k->fck);
- goto err;
- }
-
- status = clk_prepare_enable(spi100k->ick);
- if (status != 0) {
- dev_err(&pdev->dev, "failed to enable ick: %d\n", status);
- goto err;
- }
-
- status = clk_prepare_enable(spi100k->fck);
- if (status != 0) {
- dev_err(&pdev->dev, "failed to enable fck: %d\n", status);
- goto err_ick;
- }
-
- pm_runtime_enable(&pdev->dev);
- pm_runtime_set_active(&pdev->dev);
-
- status = devm_spi_register_master(&pdev->dev, master);
- if (status < 0)
- goto err_fck;
-
- return status;
-
-err_fck:
- pm_runtime_disable(&pdev->dev);
- clk_disable_unprepare(spi100k->fck);
-err_ick:
- clk_disable_unprepare(spi100k->ick);
-err:
- spi_master_put(master);
- return status;
-}
-
-static int omap1_spi100k_remove(struct platform_device *pdev)
-{
- struct spi_master *master = platform_get_drvdata(pdev);
- struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
-
- pm_runtime_disable(&pdev->dev);
-
- clk_disable_unprepare(spi100k->fck);
- clk_disable_unprepare(spi100k->ick);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int omap1_spi100k_runtime_suspend(struct device *dev)
-{
- struct spi_master *master = dev_get_drvdata(dev);
- struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
-
- clk_disable_unprepare(spi100k->ick);
- clk_disable_unprepare(spi100k->fck);
-
- return 0;
-}
-
-static int omap1_spi100k_runtime_resume(struct device *dev)
-{
- struct spi_master *master = dev_get_drvdata(dev);
- struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
- int ret;
-
- ret = clk_prepare_enable(spi100k->ick);
- if (ret != 0) {
- dev_err(dev, "Failed to enable ick: %d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(spi100k->fck);
- if (ret != 0) {
- dev_err(dev, "Failed to enable fck: %d\n", ret);
- clk_disable_unprepare(spi100k->ick);
- return ret;
- }
-
- return 0;
-}
-#endif
-
-static const struct dev_pm_ops omap1_spi100k_pm = {
- SET_RUNTIME_PM_OPS(omap1_spi100k_runtime_suspend,
- omap1_spi100k_runtime_resume, NULL)
-};
-
-static struct platform_driver omap1_spi100k_driver = {
- .driver = {
- .name = "omap1_spi100k",
- .pm = &omap1_spi100k_pm,
- },
- .probe = omap1_spi100k_probe,
- .remove = omap1_spi100k_remove,
-};
-
-module_platform_driver(omap1_spi100k_driver);
-
-MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver");
-MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c
index 29198e6815b2..20c87163d612 100644
--- a/drivers/spi/spi-omap-uwire.c
+++ b/drivers/spi/spi-omap-uwire.c
@@ -99,7 +99,7 @@ struct uwire_state {
* Or, put it in a structure which is used throughout the driver;
* that avoids having to issue two loads for each bit of static data.
*/
-static unsigned int uwire_idx_shift;
+static unsigned int uwire_idx_shift = 2;
static void __iomem *uwire_base;
static inline void uwire_write_reg(int idx, u16 val)
@@ -481,11 +481,6 @@ static int uwire_probe(struct platform_device *pdev)
}
clk_prepare_enable(uwire->ck);
- if (cpu_is_omap7xx())
- uwire_idx_shift = 1;
- else
- uwire_idx_shift = 2;
-
uwire_write_reg(UWIRE_SR3, 1);
/* the spi->mode bits understood by this driver: */
@@ -536,15 +531,6 @@ static struct platform_driver uwire_driver = {
static int __init omap_uwire_init(void)
{
- /* FIXME move these into the relevant board init code. also, include
- * H3 support; it uses tsc2101 like H2 (on a different chipselect).
- */
-
- if (machine_is_omap_h2()) {
- /* defaults: W21 SDO, U18 SDI, V19 SCL */
- omap_cfg_reg(N14_1610_UWIRE_CS0);
- omap_cfg_reg(N15_1610_UWIRE_CS1);
- }
return platform_driver_register(&uwire_driver);
}
diff --git a/drivers/spi/spi-s3c24xx-regs.h b/drivers/spi/spi-s3c24xx-regs.h
deleted file mode 100644
index f51464ab5677..000000000000
--- a/drivers/spi/spi-s3c24xx-regs.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (c) 2004 Fetron GmbH
- *
- * S3C2410 SPI register definition
- */
-
-#ifndef __SPI_S3C2410_H
-#define __SPI_S3C2410_H
-
-#define S3C2410_SPCON (0x00)
-
-#define S3C2410_SPCON_SMOD_DMA (2 << 5) /* DMA mode */
-#define S3C2410_SPCON_SMOD_INT (1 << 5) /* interrupt mode */
-#define S3C2410_SPCON_SMOD_POLL (0 << 5) /* polling mode */
-#define S3C2410_SPCON_ENSCK (1 << 4) /* Enable SCK */
-#define S3C2410_SPCON_MSTR (1 << 3) /* Master:1, Slave:0 select */
-#define S3C2410_SPCON_CPOL_HIGH (1 << 2) /* Clock polarity select */
-#define S3C2410_SPCON_CPOL_LOW (0 << 2) /* Clock polarity select */
-
-#define S3C2410_SPCON_CPHA_FMTB (1 << 1) /* Clock Phase Select */
-#define S3C2410_SPCON_CPHA_FMTA (0 << 1) /* Clock Phase Select */
-
-#define S3C2410_SPSTA (0x04)
-
-#define S3C2410_SPSTA_DCOL (1 << 2) /* Data Collision Error */
-#define S3C2410_SPSTA_MULD (1 << 1) /* Multi Master Error */
-#define S3C2410_SPSTA_READY (1 << 0) /* Data Tx/Rx ready */
-#define S3C2412_SPSTA_READY_ORG (1 << 3)
-
-#define S3C2410_SPPIN (0x08)
-
-#define S3C2410_SPPIN_ENMUL (1 << 2) /* Multi Master Error detect */
-#define S3C2410_SPPIN_RESERVED (1 << 1)
-#define S3C2410_SPPIN_KEEP (1 << 0) /* Master Out keep */
-
-#define S3C2410_SPPRE (0x0C)
-#define S3C2410_SPTDAT (0x10)
-#define S3C2410_SPRDAT (0x14)
-
-#endif /* __SPI_S3C2410_H */
diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c
deleted file mode 100644
index ef25b5e93900..000000000000
--- a/drivers/spi/spi-s3c24xx.c
+++ /dev/null
@@ -1,596 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2006 Ben Dooks
- * Copyright 2006-2009 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
-*/
-
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-
-#include <linux/spi/spi.h>
-#include <linux/spi/spi_bitbang.h>
-#include <linux/spi/s3c24xx.h>
-#include <linux/spi/s3c24xx-fiq.h>
-#include <linux/module.h>
-
-#include <asm/fiq.h>
-
-#include "spi-s3c24xx-regs.h"
-
-/**
- * struct s3c24xx_spi_devstate - per device data
- * @hz: Last frequency calculated for @sppre field.
- * @mode: Last mode setting for the @spcon field.
- * @spcon: Value to write to the SPCON register.
- * @sppre: Value to write to the SPPRE register.
- */
-struct s3c24xx_spi_devstate {
- unsigned int hz;
- unsigned int mode;
- u8 spcon;
- u8 sppre;
-};
-
-enum spi_fiq_mode {
- FIQ_MODE_NONE = 0,
- FIQ_MODE_TX = 1,
- FIQ_MODE_RX = 2,
- FIQ_MODE_TXRX = 3,
-};
-
-struct s3c24xx_spi {
- /* bitbang has to be first */
- struct spi_bitbang bitbang;
- struct completion done;
-
- void __iomem *regs;
- int irq;
- int len;
- int count;
-
- struct fiq_handler fiq_handler;
- enum spi_fiq_mode fiq_mode;
- unsigned char fiq_inuse;
- unsigned char fiq_claimed;
-
- /* data buffers */
- const unsigned char *tx;
- unsigned char *rx;
-
- struct clk *clk;
- struct spi_master *master;
- struct spi_device *curdev;
- struct device *dev;
- struct s3c2410_spi_info *pdata;
-};
-
-#define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT)
-#define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP)
-
-static inline struct s3c24xx_spi *to_hw(struct spi_device *sdev)
-{
- return spi_master_get_devdata(sdev->master);
-}
-
-static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
-{
- struct s3c24xx_spi_devstate *cs = spi->controller_state;
- struct s3c24xx_spi *hw = to_hw(spi);
-
- /* change the chipselect state and the state of the spi engine clock */
-
- switch (value) {
- case BITBANG_CS_INACTIVE:
- writeb(cs->spcon, hw->regs + S3C2410_SPCON);
- break;
-
- case BITBANG_CS_ACTIVE:
- writeb(cs->spcon | S3C2410_SPCON_ENSCK,
- hw->regs + S3C2410_SPCON);
- break;
- }
-}
-
-static int s3c24xx_spi_update_state(struct spi_device *spi,
- struct spi_transfer *t)
-{
- struct s3c24xx_spi *hw = to_hw(spi);
- struct s3c24xx_spi_devstate *cs = spi->controller_state;
- unsigned int hz;
- unsigned int div;
- unsigned long clk;
-
- hz = t ? t->speed_hz : spi->max_speed_hz;
-
- if (!hz)
- hz = spi->max_speed_hz;
-
- if (spi->mode != cs->mode) {
- u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK;
-
- if (spi->mode & SPI_CPHA)
- spcon |= S3C2410_SPCON_CPHA_FMTB;
-
- if (spi->mode & SPI_CPOL)
- spcon |= S3C2410_SPCON_CPOL_HIGH;
-
- cs->mode = spi->mode;
- cs->spcon = spcon;
- }
-
- if (cs->hz != hz) {
- clk = clk_get_rate(hw->clk);
- div = DIV_ROUND_UP(clk, hz * 2) - 1;
-
- if (div > 255)
- div = 255;
-
- dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n",
- div, hz, clk / (2 * (div + 1)));
-
- cs->hz = hz;
- cs->sppre = div;
- }
-
- return 0;
-}
-
-static int s3c24xx_spi_setupxfer(struct spi_device *spi,
- struct spi_transfer *t)
-{
- struct s3c24xx_spi_devstate *cs = spi->controller_state;
- struct s3c24xx_spi *hw = to_hw(spi);
- int ret;
-
- ret = s3c24xx_spi_update_state(spi, t);
- if (!ret)
- writeb(cs->sppre, hw->regs + S3C2410_SPPRE);
-
- return ret;
-}
-
-static int s3c24xx_spi_setup(struct spi_device *spi)
-{
- struct s3c24xx_spi_devstate *cs = spi->controller_state;
- struct s3c24xx_spi *hw = to_hw(spi);
- int ret;
-
- /* allocate settings on the first call */
- if (!cs) {
- cs = devm_kzalloc(&spi->dev,
- sizeof(struct s3c24xx_spi_devstate),
- GFP_KERNEL);
- if (!cs)
- return -ENOMEM;
-
- cs->spcon = SPCON_DEFAULT;
- cs->hz = -1;
- spi->controller_state = cs;
- }
-
- /* initialise the state from the device */
- ret = s3c24xx_spi_update_state(spi, NULL);
- if (ret)
- return ret;
-
- mutex_lock(&hw->bitbang.lock);
- if (!hw->bitbang.busy) {
- hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);
- /* need to ndelay for 0.5 clocktick ? */
- }
- mutex_unlock(&hw->bitbang.lock);
-
- return 0;
-}
-
-static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
-{
- return hw->tx ? hw->tx[count] : 0;
-}
-
-#ifdef CONFIG_SPI_S3C24XX_FIQ
-/* Support for FIQ based pseudo-DMA to improve the transfer speed.
- *
- * This code uses the assembly helper in spi_s3c24xx_spi.S which is
- * used by the FIQ core to move data between main memory and the peripheral
- * block. Since this is code running on the processor, there is no problem
- * with cache coherency of the buffers, so we can use any buffer we like.
- */
-
-/**
- * struct spi_fiq_code - FIQ code and header
- * @length: The length of the code fragment, excluding this header.
- * @ack_offset: The offset from @data to the word to place the IRQ ACK bit at.
- * @data: The code itself to install as a FIQ handler.
- */
-struct spi_fiq_code {
- u32 length;
- u32 ack_offset;
- u8 data[];
-};
-
-/**
- * s3c24xx_spi_tryfiq - attempt to claim and setup FIQ for transfer
- * @hw: The hardware state.
- *
- * Claim the FIQ handler (only one can be active at any one time) and
- * then setup the correct transfer code for this transfer.
- *
- * This call updates all the necessary state information if successful,
- * so the caller does not need to do anything more than start the transfer
- * as normal, since the IRQ will have been re-routed to the FIQ handler.
-*/
-static void s3c24xx_spi_tryfiq(struct s3c24xx_spi *hw)
-{
- struct pt_regs regs;
- enum spi_fiq_mode mode;
- struct spi_fiq_code *code;
- u32 *ack_ptr = NULL;
- int ret;
-
- if (!hw->fiq_claimed) {
- /* try and claim fiq if we haven't got it, and if not
- * then return and simply use another transfer method */
-
- ret = claim_fiq(&hw->fiq_handler);
- if (ret)
- return;
- }
-
- if (hw->tx && !hw->rx)
- mode = FIQ_MODE_TX;
- else if (hw->rx && !hw->tx)
- mode = FIQ_MODE_RX;
- else
- mode = FIQ_MODE_TXRX;
-
- regs.uregs[fiq_rspi] = (long)hw->regs;
- regs.uregs[fiq_rrx] = (long)hw->rx;
- regs.uregs[fiq_rtx] = (long)hw->tx + 1;
- regs.uregs[fiq_rcount] = hw->len - 1;
-
- set_fiq_regs(&regs);
-
- if (hw->fiq_mode != mode) {
- hw->fiq_mode = mode;
-
- switch (mode) {
- case FIQ_MODE_TX:
- code = &s3c24xx_spi_fiq_tx;
- break;
- case FIQ_MODE_RX:
- code = &s3c24xx_spi_fiq_rx;
- break;
- case FIQ_MODE_TXRX:
- code = &s3c24xx_spi_fiq_txrx;
- break;
- default:
- code = NULL;
- }
-
- BUG_ON(!code);
-
- ack_ptr = (u32 *)&code->data[code->ack_offset];
- set_fiq_handler(&code->data, code->length);
- }
-
- s3c24xx_set_fiq(hw->irq, ack_ptr, true);
-
- hw->fiq_mode = mode;
- hw->fiq_inuse = 1;
-}
-
-/**
- * s3c24xx_spi_fiqop - FIQ core code callback
- * @pw: Data registered with the handler
- * @release: Whether this is a release or a return.
- *
- * Called by the FIQ code when another module wants to use the FIQ, so
- * return whether we are currently using this or not and then update our
- * internal state.
- */
-static int s3c24xx_spi_fiqop(void *pw, int release)
-{
- struct s3c24xx_spi *hw = pw;
- int ret = 0;
-
- if (release) {
- if (hw->fiq_inuse)
- ret = -EBUSY;
-
- /* note, we do not need to unroute the FIQ, as the FIQ
- * vector code de-routes it to signal the end of transfer */
-
- hw->fiq_mode = FIQ_MODE_NONE;
- hw->fiq_claimed = 0;
- } else {
- hw->fiq_claimed = 1;
- }
-
- return ret;
-}
-
-/**
- * s3c24xx_spi_initfiq - setup the information for the FIQ core
- * @hw: The hardware state.
- *
- * Setup the fiq_handler block to pass to the FIQ core.
- */
-static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *hw)
-{
- hw->fiq_handler.dev_id = hw;
- hw->fiq_handler.name = dev_name(hw->dev);
- hw->fiq_handler.fiq_op = s3c24xx_spi_fiqop;
-}
-
-/**
- * s3c24xx_spi_usefiq - return if we should be using FIQ.
- * @hw: The hardware state.
- *
- * Return true if the platform data specifies whether this channel is
- * allowed to use the FIQ.
- */
-static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *hw)
-{
- return hw->pdata->use_fiq;
-}
-
-/**
- * s3c24xx_spi_usingfiq - return if channel is using FIQ
- * @spi: The hardware state.
- *
- * Return whether the channel is currently using the FIQ (separate from
- * whether the FIQ is claimed).
- */
-static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *spi)
-{
- return spi->fiq_inuse;
-}
-#else
-
-static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *s) { }
-static inline void s3c24xx_spi_tryfiq(struct s3c24xx_spi *s) { }
-static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *s) { return false; }
-static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *s) { return false; }
-
-#endif /* CONFIG_SPI_S3C24XX_FIQ */
-
-static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
-{
- struct s3c24xx_spi *hw = to_hw(spi);
-
- hw->tx = t->tx_buf;
- hw->rx = t->rx_buf;
- hw->len = t->len;
- hw->count = 0;
-
- init_completion(&hw->done);
-
- hw->fiq_inuse = 0;
- if (s3c24xx_spi_usefiq(hw) && t->len >= 3)
- s3c24xx_spi_tryfiq(hw);
-
- /* send the first byte */
- writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
-
- wait_for_completion(&hw->done);
- return hw->count;
-}
-
-static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
-{
- struct s3c24xx_spi *hw = dev;
- unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);
- unsigned int count = hw->count;
-
- if (spsta & S3C2410_SPSTA_DCOL) {
- dev_dbg(hw->dev, "data-collision\n");
- complete(&hw->done);
- goto irq_done;
- }
-
- if (!(spsta & S3C2410_SPSTA_READY)) {
- dev_dbg(hw->dev, "spi not ready for tx?\n");
- complete(&hw->done);
- goto irq_done;
- }
-
- if (!s3c24xx_spi_usingfiq(hw)) {
- hw->count++;
-
- if (hw->rx)
- hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
-
- count++;
-
- if (count < hw->len)
- writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
- else
- complete(&hw->done);
- } else {
- hw->count = hw->len;
- hw->fiq_inuse = 0;
-
- if (hw->rx)
- hw->rx[hw->len-1] = readb(hw->regs + S3C2410_SPRDAT);
-
- complete(&hw->done);
- }
-
- irq_done:
- return IRQ_HANDLED;
-}
-
-static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw)
-{
- /* for the moment, permanently enable the clock */
-
- clk_enable(hw->clk);
-
- /* program defaults into the registers */
-
- writeb(0xff, hw->regs + S3C2410_SPPRE);
- writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);
- writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);
-}
-
-static int s3c24xx_spi_probe(struct platform_device *pdev)
-{
- struct s3c2410_spi_info *pdata;
- struct s3c24xx_spi *hw;
- struct spi_master *master;
- int err = 0;
-
- master = devm_spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));
- if (master == NULL) {
- dev_err(&pdev->dev, "No memory for spi_master\n");
- return -ENOMEM;
- }
-
- hw = spi_master_get_devdata(master);
-
- hw->master = master;
- hw->pdata = pdata = dev_get_platdata(&pdev->dev);
- hw->dev = &pdev->dev;
-
- if (pdata == NULL) {
- dev_err(&pdev->dev, "No platform data supplied\n");
- return -ENOENT;
- }
-
- platform_set_drvdata(pdev, hw);
- init_completion(&hw->done);
-
- /* initialise fiq handler */
-
- s3c24xx_spi_initfiq(hw);
-
- /* setup the master state. */
-
- /* the spi->mode bits understood by this driver: */
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
-
- master->num_chipselect = hw->pdata->num_cs;
- master->bus_num = pdata->bus_num;
- master->bits_per_word_mask = SPI_BPW_MASK(8);
- /* we need to call the local chipselect callback */
- master->flags = SPI_MASTER_GPIO_SS;
- master->use_gpio_descriptors = true;
-
- /* setup the state for the bitbang driver */
-
- hw->bitbang.master = hw->master;
- hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
- hw->bitbang.chipselect = s3c24xx_spi_chipsel;
- hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;
-
- hw->master->setup = s3c24xx_spi_setup;
-
- dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);
-
- /* find and map our resources */
- hw->regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(hw->regs))
- return PTR_ERR(hw->regs);
-
- hw->irq = platform_get_irq(pdev, 0);
- if (hw->irq < 0)
- return -ENOENT;
-
- err = devm_request_irq(&pdev->dev, hw->irq, s3c24xx_spi_irq, 0,
- pdev->name, hw);
- if (err) {
- dev_err(&pdev->dev, "Cannot claim IRQ\n");
- return err;
- }
-
- hw->clk = devm_clk_get(&pdev->dev, "spi");
- if (IS_ERR(hw->clk)) {
- dev_err(&pdev->dev, "No clock for device\n");
- return PTR_ERR(hw->clk);
- }
-
- s3c24xx_spi_initialsetup(hw);
-
- /* register our spi controller */
-
- err = spi_bitbang_start(&hw->bitbang);
- if (err) {
- dev_err(&pdev->dev, "Failed to register SPI master\n");
- goto err_register;
- }
-
- return 0;
-
- err_register:
- clk_disable(hw->clk);
-
- return err;
-}
-
-static int s3c24xx_spi_remove(struct platform_device *dev)
-{
- struct s3c24xx_spi *hw = platform_get_drvdata(dev);
-
- spi_bitbang_stop(&hw->bitbang);
- clk_disable(hw->clk);
- spi_master_put(hw->master);
- return 0;
-}
-
-
-#ifdef CONFIG_PM
-
-static int s3c24xx_spi_suspend(struct device *dev)
-{
- struct s3c24xx_spi *hw = dev_get_drvdata(dev);
- int ret;
-
- ret = spi_master_suspend(hw->master);
- if (ret)
- return ret;
-
- clk_disable(hw->clk);
- return 0;
-}
-
-static int s3c24xx_spi_resume(struct device *dev)
-{
- struct s3c24xx_spi *hw = dev_get_drvdata(dev);
-
- s3c24xx_spi_initialsetup(hw);
- return spi_master_resume(hw->master);
-}
-
-static const struct dev_pm_ops s3c24xx_spi_pmops = {
- .suspend = s3c24xx_spi_suspend,
- .resume = s3c24xx_spi_resume,
-};
-
-#define S3C24XX_SPI_PMOPS &s3c24xx_spi_pmops
-#else
-#define S3C24XX_SPI_PMOPS NULL
-#endif /* CONFIG_PM */
-
-MODULE_ALIAS("platform:s3c2410-spi");
-static struct platform_driver s3c24xx_spi_driver = {
- .probe = s3c24xx_spi_probe,
- .remove = s3c24xx_spi_remove,
- .driver = {
- .name = "s3c2410-spi",
- .pm = S3C24XX_SPI_PMOPS,
- },
-};
-module_platform_driver(s3c24xx_spi_driver);
-
-MODULE_DESCRIPTION("S3C24XX SPI Driver");
-MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
-MODULE_LICENSE("GPL");