summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig50
-rw-r--r--drivers/spi/Makefile4
-rw-r--r--drivers/spi/spi-amd.c87
-rw-r--r--drivers/spi/spi-ath79.c34
-rw-r--r--drivers/spi/spi-bcm2835aux.c21
-rw-r--r--drivers/spi/spi-bitbang-txrx.h66
-rw-r--r--drivers/spi/spi-cadence-xspi.c4
-rw-r--r--drivers/spi/spi-fsi.c10
-rw-r--r--drivers/spi/spi-geni-qcom.c7
-rw-r--r--drivers/spi/spi-gpio.c42
-rw-r--r--drivers/spi/spi-intel-pci.c95
-rw-r--r--drivers/spi/spi-intel-platform.c39
-rw-r--r--drivers/spi/spi-intel.c1250
-rw-r--r--drivers/spi/spi-intel.h19
-rw-r--r--drivers/spi/spi-lantiq-ssc.c8
-rw-r--r--drivers/spi/spi-mem.c6
-rw-r--r--drivers/spi/spi-mpc512x-psc.c47
-rw-r--r--drivers/spi/spi-mt65xx.c134
-rw-r--r--drivers/spi/spi-mtk-nor.c71
-rw-r--r--drivers/spi/spi-npcm-fiu.c14
-rw-r--r--drivers/spi/spi-pic32.c9
-rw-r--r--drivers/spi/spi-pxa2xx-pci.c320
-rw-r--r--drivers/spi/spi-pxa2xx.c68
-rw-r--r--drivers/spi/spi-qup.c5
-rw-r--r--drivers/spi/spi-rockchip-sfc.c4
-rw-r--r--drivers/spi/spi-rockchip.c122
-rw-r--r--drivers/spi/spi-s3c24xx.c47
-rw-r--r--drivers/spi/spi-s3c64xx.c80
-rw-r--r--drivers/spi/spi-slave-system-control.c3
-rw-r--r--drivers/spi/spi-slave-time.c3
-rw-r--r--drivers/spi/spi-st-ssc4.c31
-rw-r--r--drivers/spi/spi-stm32.c2
-rw-r--r--drivers/spi/spi-sun4i.c2
-rw-r--r--drivers/spi/spi-sunplus-sp7021.c584
-rw-r--r--drivers/spi/spi-tegra114.c4
-rw-r--r--drivers/spi/spi-tegra20-slink.c8
-rw-r--r--drivers/spi/spi-tegra210-quad.c341
-rw-r--r--drivers/spi/spi-tle62x0.c3
-rw-r--r--drivers/spi/spi-topcliff-pch.c15
-rw-r--r--drivers/spi/spi-zynqmp-gqspi.c5
-rw-r--r--drivers/spi/spi.c291
-rw-r--r--drivers/spi/spidev.c35
42 files changed, 3301 insertions, 689 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b2a8821971e1..31a2cef3790c 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -427,6 +427,45 @@ config SPI_INGENIC
To compile this driver as a module, choose M here: the module
will be called spi-ingenic.
+config SPI_INTEL
+ tristate
+
+config SPI_INTEL_PCI
+ tristate "Intel PCH/PCU SPI flash PCI driver (DANGEROUS)"
+ depends on PCI
+ depends on X86 || COMPILE_TEST
+ depends on SPI_MEM
+ select SPI_INTEL
+ help
+ This enables PCI support for the Intel PCH/PCU SPI controller in
+ master mode. This controller is present in modern Intel hardware
+ and is used to hold BIOS and other persistent settings. Using
+ this driver it is possible to upgrade BIOS directly from Linux.
+
+ Say N here unless you know what you are doing. Overwriting the
+ SPI flash may render the system unbootable.
+
+ To compile this driver as a module, choose M here: the module
+ will be called spi-intel-pci.
+
+config SPI_INTEL_PLATFORM
+ tristate "Intel PCH/PCU SPI flash platform driver (DANGEROUS)"
+ depends on X86 || COMPILE_TEST
+ depends on SPI_MEM
+ select SPI_INTEL
+ help
+ This enables platform support for the Intel PCH/PCU SPI
+ controller in master mode. This controller is present in modern
+ Intel hardware and is used to hold BIOS and other persistent
+ settings. Using this driver it is possible to upgrade BIOS
+ directly from Linux.
+
+ Say N here unless you know what you are doing. Overwriting the
+ SPI flash may render the system unbootable.
+
+ To compile this driver as a module, choose M here: the module
+ will be called spi-intel-platform.
+
config SPI_JCORE
tristate "J-Core SPI Master"
depends on OF && (SUPERH || COMPILE_TEST)
@@ -866,6 +905,17 @@ config SPI_SUN6I
help
This enables using the SPI controller on the Allwinner A31 SoCs.
+config SPI_SUNPLUS_SP7021
+ tristate "Sunplus SP7021 SPI controller"
+ depends on SOC_SP7021 || COMPILE_TEST
+ help
+ This enables Sunplus SP7021 SPI controller driver on the SP7021 SoCs.
+ This driver can also be built as a module. If so, the module will be
+ called as spi-sunplus-sp7021.
+
+ If you have a Sunplus SP7021 platform say Y here.
+ If unsure, say N.
+
config SPI_SYNQUACER
tristate "Socionext's SynQuacer HighSpeed SPI controller"
depends on ARCH_SYNQUACER || COMPILE_TEST
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index dd7393a6046f..3aa28ed3f761 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -61,6 +61,9 @@ obj-$(CONFIG_SPI_HISI_SFC_V3XX) += spi-hisi-sfc-v3xx.o
obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o
obj-$(CONFIG_SPI_IMX) += spi-imx.o
obj-$(CONFIG_SPI_INGENIC) += spi-ingenic.o
+obj-$(CONFIG_SPI_INTEL) += spi-intel.o
+obj-$(CONFIG_SPI_INTEL_PCI) += spi-intel-pci.o
+obj-$(CONFIG_SPI_INTEL_PLATFORM) += spi-intel-platform.o
obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o
obj-$(CONFIG_SPI_JCORE) += spi-jcore.o
obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
@@ -119,6 +122,7 @@ obj-$(CONFIG_SPI_STM32_QSPI) += spi-stm32-qspi.o
obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o
obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o
obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o
+obj-$(CONFIG_SPI_SUNPLUS_SP7021) += spi-sunplus-sp7021.o
obj-$(CONFIG_SPI_SYNQUACER) += spi-synquacer.o
obj-$(CONFIG_SPI_TEGRA210_QUAD) += spi-tegra210-quad.o
obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o
diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c
index 4b3ac7aceaf6..cba6a4486c24 100644
--- a/drivers/spi/spi-amd.c
+++ b/drivers/spi/spi-amd.c
@@ -12,12 +12,17 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
+#include <linux/iopoll.h>
#define AMD_SPI_CTRL0_REG 0x00
#define AMD_SPI_EXEC_CMD BIT(16)
#define AMD_SPI_FIFO_CLEAR BIT(20)
#define AMD_SPI_BUSY BIT(31)
+#define AMD_SPI_OPCODE_REG 0x45
+#define AMD_SPI_CMD_TRIGGER_REG 0x47
+#define AMD_SPI_TRIGGER_CMD BIT(7)
+
#define AMD_SPI_OPCODE_MASK 0xFF
#define AMD_SPI_ALT_CS_REG 0x1D
@@ -34,10 +39,15 @@
#define AMD_SPI_XFER_TX 1
#define AMD_SPI_XFER_RX 2
+enum amd_spi_versions {
+ AMD_SPI_V1 = 1, /* AMDI0061 */
+ AMD_SPI_V2, /* AMDI0062 */
+};
+
struct amd_spi {
void __iomem *io_remap_addr;
unsigned long io_base_addr;
- u32 rom_addr;
+ enum amd_spi_versions version;
};
static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx)
@@ -81,14 +91,29 @@ static void amd_spi_select_chip(struct amd_spi *amd_spi, u8 cs)
amd_spi_setclear_reg8(amd_spi, AMD_SPI_ALT_CS_REG, cs, AMD_SPI_ALT_CS_MASK);
}
+static inline void amd_spi_clear_chip(struct amd_spi *amd_spi, u8 chip_select)
+{
+ amd_spi_writereg8(amd_spi, AMD_SPI_ALT_CS_REG, chip_select & ~AMD_SPI_ALT_CS_MASK);
+}
+
static void amd_spi_clear_fifo_ptr(struct amd_spi *amd_spi)
{
amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, AMD_SPI_FIFO_CLEAR);
}
-static void amd_spi_set_opcode(struct amd_spi *amd_spi, u8 cmd_opcode)
+static int amd_spi_set_opcode(struct amd_spi *amd_spi, u8 cmd_opcode)
{
- amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, cmd_opcode, AMD_SPI_OPCODE_MASK);
+ switch (amd_spi->version) {
+ case AMD_SPI_V1:
+ amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, cmd_opcode,
+ AMD_SPI_OPCODE_MASK);
+ return 0;
+ case AMD_SPI_V2:
+ amd_spi_writereg8(amd_spi, AMD_SPI_OPCODE_REG, cmd_opcode);
+ return 0;
+ default:
+ return -ENODEV;
+ }
}
static inline void amd_spi_set_rx_count(struct amd_spi *amd_spi, u8 rx_count)
@@ -103,16 +128,22 @@ static inline void amd_spi_set_tx_count(struct amd_spi *amd_spi, u8 tx_count)
static int amd_spi_busy_wait(struct amd_spi *amd_spi)
{
- int timeout = 100000;
-
- /* poll for SPI bus to become idle */
- while (amd_spi_readreg32(amd_spi, AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) {
- usleep_range(10, 20);
- if (timeout-- < 0)
- return -ETIMEDOUT;
+ u32 val;
+ int reg;
+
+ switch (amd_spi->version) {
+ case AMD_SPI_V1:
+ reg = AMD_SPI_CTRL0_REG;
+ break;
+ case AMD_SPI_V2:
+ reg = AMD_SPI_STATUS_REG;
+ break;
+ default:
+ return -ENODEV;
}
- return 0;
+ return readl_poll_timeout(amd_spi->io_remap_addr + reg, val,
+ !(val & AMD_SPI_BUSY), 20, 2000000);
}
static int amd_spi_execute_opcode(struct amd_spi *amd_spi)
@@ -123,10 +154,20 @@ static int amd_spi_execute_opcode(struct amd_spi *amd_spi)
if (ret)
return ret;
- /* Set ExecuteOpCode bit in the CTRL0 register */
- amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, AMD_SPI_EXEC_CMD);
-
- return 0;
+ switch (amd_spi->version) {
+ case AMD_SPI_V1:
+ /* Set ExecuteOpCode bit in the CTRL0 register */
+ amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD,
+ AMD_SPI_EXEC_CMD);
+ return 0;
+ case AMD_SPI_V2:
+ /* Trigger the command execution */
+ amd_spi_setclear_reg8(amd_spi, AMD_SPI_CMD_TRIGGER_REG,
+ AMD_SPI_TRIGGER_CMD, AMD_SPI_TRIGGER_CMD);
+ return 0;
+ default:
+ return -ENODEV;
+ }
}
static int amd_spi_master_setup(struct spi_device *spi)
@@ -196,6 +237,17 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
message->actual_length = tx_len + rx_len + 1;
/* complete the transaction */
message->status = 0;
+
+ switch (amd_spi->version) {
+ case AMD_SPI_V1:
+ break;
+ case AMD_SPI_V2:
+ amd_spi_clear_chip(amd_spi, message->spi->chip_select);
+ break;
+ default:
+ return -ENODEV;
+ }
+
spi_finalize_current_message(master);
return 0;
@@ -241,6 +293,8 @@ static int amd_spi_probe(struct platform_device *pdev)
}
dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr);
+ amd_spi->version = (enum amd_spi_versions) device_get_match_data(dev);
+
/* Initialize the spi_master fields */
master->bus_num = 0;
master->num_chipselect = 4;
@@ -266,7 +320,8 @@ err_free_master:
#ifdef CONFIG_ACPI
static const struct acpi_device_id spi_acpi_match[] = {
- { "AMDI0061", 0 },
+ { "AMDI0061", AMD_SPI_V1 },
+ { "AMDI0062", AMD_SPI_V2 },
{},
};
MODULE_DEVICE_TABLE(acpi, spi_acpi_match);
diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c
index d1e287d2d9cd..607e7a49fb89 100644
--- a/drivers/spi/spi-ath79.c
+++ b/drivers/spi/spi-ath79.c
@@ -15,6 +15,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
+#include <linux/spi/spi-mem.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/bitops.h>
#include <linux/clk.h>
@@ -133,6 +134,38 @@ static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned int nsecs,
return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS);
}
+static int ath79_exec_mem_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct ath79_spi *sp = ath79_spidev_to_sp(mem->spi);
+
+ /* Ensures that reading is performed on device connected to hardware cs0 */
+ if (mem->spi->chip_select || mem->spi->cs_gpiod)
+ return -ENOTSUPP;
+
+ /* Only use for fast-read op. */
+ if (op->cmd.opcode != 0x0b || op->data.dir != SPI_MEM_DATA_IN ||
+ op->addr.nbytes != 3 || op->dummy.nbytes != 1)
+ return -ENOTSUPP;
+
+ /* disable GPIO mode */
+ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0);
+
+ memcpy_fromio(op->data.buf.in, sp->base + op->addr.val, op->data.nbytes);
+
+ /* enable GPIO mode */
+ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
+
+ /* restore IOC register */
+ ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
+
+ return 0;
+}
+
+static const struct spi_controller_mem_ops ath79_mem_ops = {
+ .exec_op = ath79_exec_mem_op,
+};
+
static int ath79_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
@@ -154,6 +187,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
master->flags = SPI_MASTER_GPIO_SS;
master->num_chipselect = 3;
+ master->mem_ops = &ath79_mem_ops;
sp->bitbang.master = master;
sp->bitbang.chipselect = ath79_spi_chipselect;
diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c
index 7d709a8c833b..e28521922330 100644
--- a/drivers/spi/spi-bcm2835aux.c
+++ b/drivers/spi/spi-bcm2835aux.c
@@ -22,7 +22,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
@@ -445,25 +444,12 @@ static void bcm2835aux_spi_handle_err(struct spi_master *master,
static int bcm2835aux_spi_setup(struct spi_device *spi)
{
- int ret;
-
/* sanity check for native cs */
if (spi->mode & SPI_NO_CS)
return 0;
- if (gpio_is_valid(spi->cs_gpio)) {
- /* with gpio-cs set the GPIO to the correct level
- * and as output (in case the dt has the gpio not configured
- * as output but native cs)
- */
- ret = gpio_direction_output(spi->cs_gpio,
- (spi->mode & SPI_CS_HIGH) ? 0 : 1);
- if (ret)
- dev_err(&spi->dev,
- "could not set gpio %i as output: %i\n",
- spi->cs_gpio, ret);
-
- return ret;
- }
+
+ if (spi->cs_gpiod)
+ return 0;
/* for dt-backwards compatibility: only support native on CS0
* known things not supported with broken native CS:
@@ -519,6 +505,7 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev)
master->prepare_message = bcm2835aux_spi_prepare_message;
master->unprepare_message = bcm2835aux_spi_unprepare_message;
master->dev.of_node = pdev->dev.of_node;
+ master->use_gpio_descriptors = true;
bs = spi_master_get_devdata(master);
diff --git a/drivers/spi/spi-bitbang-txrx.h b/drivers/spi/spi-bitbang-txrx.h
index ae61d72c7d28..267342dfa738 100644
--- a/drivers/spi/spi-bitbang-txrx.h
+++ b/drivers/spi/spi-bitbang-txrx.h
@@ -41,6 +41,8 @@
* chips need ... there may be several reasons you'd need to tweak timings
* in these routines, not just to make it faster or slower to match a
* particular CPU clock rate.
+ *
+ * ToDo: Maybe the bitrev macros can be used to improve the code?
*/
static inline u32
@@ -106,3 +108,67 @@ bitbang_txrx_be_cpha1(struct spi_device *spi,
}
return word;
}
+
+static inline u32
+bitbang_txrx_le_cpha0(struct spi_device *spi,
+ unsigned int nsecs, unsigned int cpol, unsigned int flags,
+ u32 word, u8 bits)
+{
+ /* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
+
+ u32 oldbit = !(word & 1);
+ /* clock starts at inactive polarity */
+ for (; likely(bits); bits--) {
+
+ /* setup LSB (to slave) on trailing edge */
+ if ((flags & SPI_MASTER_NO_TX) == 0) {
+ if ((word & 1) != oldbit) {
+ setmosi(spi, word & 1);
+ oldbit = word & 1;
+ }
+ }
+ spidelay(nsecs); /* T(setup) */
+
+ setsck(spi, !cpol);
+ spidelay(nsecs);
+
+ /* sample LSB (from slave) on leading edge */
+ word >>= 1;
+ if ((flags & SPI_MASTER_NO_RX) == 0)
+ word |= getmiso(spi) << (bits - 1);
+ setsck(spi, cpol);
+ }
+ return word;
+}
+
+static inline u32
+bitbang_txrx_le_cpha1(struct spi_device *spi,
+ unsigned int nsecs, unsigned int cpol, unsigned int flags,
+ u32 word, u8 bits)
+{
+ /* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
+
+ u32 oldbit = !(word & 1);
+ /* clock starts at inactive polarity */
+ for (; likely(bits); bits--) {
+
+ /* setup LSB (to slave) on leading edge */
+ setsck(spi, !cpol);
+ if ((flags & SPI_MASTER_NO_TX) == 0) {
+ if ((word & 1) != oldbit) {
+ setmosi(spi, word & 1);
+ oldbit = word & 1;
+ }
+ }
+ spidelay(nsecs); /* T(setup) */
+
+ setsck(spi, cpol);
+ spidelay(nsecs);
+
+ /* sample LSB (from slave) on trailing edge */
+ word >>= 1;
+ if ((flags & SPI_MASTER_NO_RX) == 0)
+ word |= getmiso(spi) << (bits - 1);
+ }
+ return word;
+}
diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 4bc1b93fc276..3ab19be83095 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -578,10 +578,8 @@ static int cdns_xspi_probe(struct platform_device *pdev)
}
cdns_xspi->irq = platform_get_irq(pdev, 0);
- if (cdns_xspi->irq < 0) {
- dev_err(dev, "Failed to get IRQ\n");
+ if (cdns_xspi->irq < 0)
return -ENXIO;
- }
ret = devm_request_irq(dev, cdns_xspi->irq, cdns_xspi_irq_handler,
IRQF_SHARED, pdev->name, cdns_xspi);
diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c
index b6c7467f0b59..d403a7a3021d 100644
--- a/drivers/spi/spi-fsi.c
+++ b/drivers/spi/spi-fsi.c
@@ -25,6 +25,7 @@
#define SPI_FSI_BASE 0x70000
#define SPI_FSI_INIT_TIMEOUT_MS 1000
+#define SPI_FSI_STATUS_TIMEOUT_MS 100
#define SPI_FSI_MAX_RX_SIZE 8
#define SPI_FSI_MAX_TX_SIZE 40
@@ -299,6 +300,7 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
struct spi_transfer *transfer)
{
int rc = 0;
+ unsigned long end;
u64 status = 0ULL;
if (transfer->tx_buf) {
@@ -315,10 +317,14 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
if (rc)
return rc;
+ end = jiffies + msecs_to_jiffies(SPI_FSI_STATUS_TIMEOUT_MS);
do {
rc = fsi_spi_status(ctx, &status, "TX");
if (rc)
return rc;
+
+ if (time_after(jiffies, end))
+ return -ETIMEDOUT;
} while (status & SPI_FSI_STATUS_TDR_FULL);
sent += nb;
@@ -329,10 +335,14 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
u8 *rx = transfer->rx_buf;
while (transfer->len > recv) {
+ end = jiffies + msecs_to_jiffies(SPI_FSI_STATUS_TIMEOUT_MS);
do {
rc = fsi_spi_status(ctx, &status, "RX");
if (rc)
return rc;
+
+ if (time_after(jiffies, end))
+ return -ETIMEDOUT;
} while (!(status & SPI_FSI_STATUS_RDR_FULL));
rc = fsi_spi_read_reg(ctx, SPI_FSI_DATA_RX, &in);
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index f7d905d2a90f..4e83cc5b445d 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -898,11 +898,8 @@ static int spi_geni_probe(struct platform_device *pdev)
return irq;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
- if (ret) {
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
- if (ret)
- return dev_err_probe(dev, ret, "could not set DMA mask\n");
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "could not set DMA mask\n");
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index 0584f4d2fde2..4b12c4964a66 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -135,25 +135,37 @@ static inline int getmiso(const struct spi_device *spi)
static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits, unsigned flags)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
+ if (unlikely(spi->mode & SPI_LSB_FIRST))
+ return bitbang_txrx_le_cpha0(spi, nsecs, 0, flags, word, bits);
+ else
+ return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
}
static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits, unsigned flags)
{
- return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits);
+ if (unlikely(spi->mode & SPI_LSB_FIRST))
+ return bitbang_txrx_le_cpha1(spi, nsecs, 0, flags, word, bits);
+ else
+ return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits);
}
static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits, unsigned flags)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits);
+ if (unlikely(spi->mode & SPI_LSB_FIRST))
+ return bitbang_txrx_le_cpha0(spi, nsecs, 1, flags, word, bits);
+ else
+ return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits);
}
static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits, unsigned flags)
{
- return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits);
+ if (unlikely(spi->mode & SPI_LSB_FIRST))
+ return bitbang_txrx_le_cpha1(spi, nsecs, 1, flags, word, bits);
+ else
+ return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits);
}
/*
@@ -170,28 +182,40 @@ static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits, unsigned flags)
{
flags = spi->master->flags;
- return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
+ if (unlikely(spi->mode & SPI_LSB_FIRST))
+ return bitbang_txrx_le_cpha0(spi, nsecs, 0, flags, word, bits);
+ else
+ return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
}
static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits, unsigned flags)
{
flags = spi->master->flags;
- return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits);
+ if (unlikely(spi->mode & SPI_LSB_FIRST))
+ return bitbang_txrx_le_cpha1(spi, nsecs, 0, flags, word, bits);
+ else
+ return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits);
}
static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits, unsigned flags)
{
flags = spi->master->flags;
- return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits);
+ if (unlikely(spi->mode & SPI_LSB_FIRST))
+ return bitbang_txrx_le_cpha0(spi, nsecs, 1, flags, word, bits);
+ else
+ return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits);
}
static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits, unsigned flags)
{
flags = spi->master->flags;
- return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits);
+ if (unlikely(spi->mode & SPI_LSB_FIRST))
+ return bitbang_txrx_le_cpha1(spi, nsecs, 1, flags, word, bits);
+ else
+ return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits);
}
/*----------------------------------------------------------------------*/
@@ -378,7 +402,7 @@ static int spi_gpio_probe(struct platform_device *pdev)
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
master->mode_bits = SPI_3WIRE | SPI_3WIRE_HIZ | SPI_CPHA | SPI_CPOL |
- SPI_CS_HIGH;
+ SPI_CS_HIGH | SPI_LSB_FIRST;
if (!spi_gpio->mosi) {
/* HW configuration without MOSI pin
*
diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c
new file mode 100644
index 000000000000..a5ef7a526a7f
--- /dev/null
+++ b/drivers/spi/spi-intel-pci.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel PCH/PCU SPI flash PCI driver.
+ *
+ * Copyright (C) 2016 - 2022, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "spi-intel.h"
+
+#define BCR 0xdc
+#define BCR_WPD BIT(0)
+
+static bool intel_spi_pci_set_writeable(void __iomem *base, void *data)
+{
+ struct pci_dev *pdev = data;
+ u32 bcr;
+
+ /* Try to make the chip read/write */
+ pci_read_config_dword(pdev, BCR, &bcr);
+ if (!(bcr & BCR_WPD)) {
+ bcr |= BCR_WPD;
+ pci_write_config_dword(pdev, BCR, bcr);
+ pci_read_config_dword(pdev, BCR, &bcr);
+ }
+
+ return bcr & BCR_WPD;
+}
+
+static const struct intel_spi_boardinfo bxt_info = {
+ .type = INTEL_SPI_BXT,
+ .set_writeable = intel_spi_pci_set_writeable,
+};
+
+static const struct intel_spi_boardinfo cnl_info = {
+ .type = INTEL_SPI_CNL,
+ .set_writeable = intel_spi_pci_set_writeable,
+};
+
+static int intel_spi_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct intel_spi_boardinfo *info;
+ int ret;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ info = devm_kmemdup(&pdev->dev, (void *)id->driver_data, sizeof(*info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->data = pdev;
+ return intel_spi_probe(&pdev->dev, &pdev->resource[0], info);
+}
+
+static const struct pci_device_id intel_spi_pci_ids[] = {
+ { PCI_VDEVICE(INTEL, 0x02a4), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x06a4), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x1bca), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x38a4), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0xa324), (unsigned long)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa3a4), (unsigned long)&bxt_info },
+ { },
+};
+MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids);
+
+static struct pci_driver intel_spi_pci_driver = {
+ .name = "intel-spi",
+ .id_table = intel_spi_pci_ids,
+ .probe = intel_spi_pci_probe,
+};
+
+module_pci_driver(intel_spi_pci_driver);
+
+MODULE_DESCRIPTION("Intel PCH/PCU SPI flash PCI driver");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-intel-platform.c b/drivers/spi/spi-intel-platform.c
new file mode 100644
index 000000000000..2ef09fa35661
--- /dev/null
+++ b/drivers/spi/spi-intel-platform.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel PCH/PCU SPI flash platform driver.
+ *
+ * Copyright (C) 2016 - 2022, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "spi-intel.h"
+
+static int intel_spi_platform_probe(struct platform_device *pdev)
+{
+ struct intel_spi_boardinfo *info;
+ struct resource *mem;
+
+ info = dev_get_platdata(&pdev->dev);
+ if (!info)
+ return -EINVAL;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ return intel_spi_probe(&pdev->dev, mem, info);
+}
+
+static struct platform_driver intel_spi_platform_driver = {
+ .probe = intel_spi_platform_probe,
+ .driver = {
+ .name = "intel-spi",
+ },
+};
+
+module_platform_driver(intel_spi_platform_driver);
+
+MODULE_DESCRIPTION("Intel PCH/PCU SPI flash platform driver");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:intel-spi");
diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c
new file mode 100644
index 000000000000..e937cfe85559
--- /dev/null
+++ b/drivers/spi/spi-intel.c
@@ -0,0 +1,1250 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel PCH/PCU SPI flash driver.
+ *
+ * Copyright (C) 2016 - 2022, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#include <linux/iopoll.h>
+#include <linux/module.h>
+
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
+
+#include <linux/spi/flash.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-mem.h>
+
+#include "spi-intel.h"
+
+/* Offsets are from @ispi->base */
+#define BFPREG 0x00
+
+#define HSFSTS_CTL 0x04
+#define HSFSTS_CTL_FSMIE BIT(31)
+#define HSFSTS_CTL_FDBC_SHIFT 24
+#define HSFSTS_CTL_FDBC_MASK (0x3f << HSFSTS_CTL_FDBC_SHIFT)
+
+#define HSFSTS_CTL_FCYCLE_SHIFT 17
+#define HSFSTS_CTL_FCYCLE_MASK (0x0f << HSFSTS_CTL_FCYCLE_SHIFT)
+/* HW sequencer opcodes */
+#define HSFSTS_CTL_FCYCLE_READ (0x00 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_WRITE (0x02 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_ERASE (0x03 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_ERASE_64K (0x04 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_RDID (0x06 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_WRSR (0x07 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_RDSR (0x08 << HSFSTS_CTL_FCYCLE_SHIFT)
+
+#define HSFSTS_CTL_FGO BIT(16)
+#define HSFSTS_CTL_FLOCKDN BIT(15)
+#define HSFSTS_CTL_FDV BIT(14)
+#define HSFSTS_CTL_SCIP BIT(5)
+#define HSFSTS_CTL_AEL BIT(2)
+#define HSFSTS_CTL_FCERR BIT(1)
+#define HSFSTS_CTL_FDONE BIT(0)
+
+#define FADDR 0x08
+#define DLOCK 0x0c
+#define FDATA(n) (0x10 + ((n) * 4))
+
+#define FRACC 0x50
+
+#define FREG(n) (0x54 + ((n) * 4))
+#define FREG_BASE_MASK 0x3fff
+#define FREG_LIMIT_SHIFT 16
+#define FREG_LIMIT_MASK (0x03fff << FREG_LIMIT_SHIFT)
+
+/* Offset is from @ispi->pregs */
+#define PR(n) ((n) * 4)
+#define PR_WPE BIT(31)
+#define PR_LIMIT_SHIFT 16
+#define PR_LIMIT_MASK (0x3fff << PR_LIMIT_SHIFT)
+#define PR_RPE BIT(15)
+#define PR_BASE_MASK 0x3fff
+
+/* Offsets are from @ispi->sregs */
+#define SSFSTS_CTL 0x00
+#define SSFSTS_CTL_FSMIE BIT(23)
+#define SSFSTS_CTL_DS BIT(22)
+#define SSFSTS_CTL_DBC_SHIFT 16
+#define SSFSTS_CTL_SPOP BIT(11)
+#define SSFSTS_CTL_ACS BIT(10)
+#define SSFSTS_CTL_SCGO BIT(9)
+#define SSFSTS_CTL_COP_SHIFT 12
+#define SSFSTS_CTL_FRS BIT(7)
+#define SSFSTS_CTL_DOFRS BIT(6)
+#define SSFSTS_CTL_AEL BIT(4)
+#define SSFSTS_CTL_FCERR BIT(3)
+#define SSFSTS_CTL_FDONE BIT(2)
+#define SSFSTS_CTL_SCIP BIT(0)
+
+#define PREOP_OPTYPE 0x04
+#define OPMENU0 0x08
+#define OPMENU1 0x0c
+
+#define OPTYPE_READ_NO_ADDR 0
+#define OPTYPE_WRITE_NO_ADDR 1
+#define OPTYPE_READ_WITH_ADDR 2
+#define OPTYPE_WRITE_WITH_ADDR 3
+
+/* CPU specifics */
+#define BYT_PR 0x74
+#define BYT_SSFSTS_CTL 0x90
+#define BYT_FREG_NUM 5
+#define BYT_PR_NUM 5
+
+#define LPT_PR 0x74
+#define LPT_SSFSTS_CTL 0x90
+#define LPT_FREG_NUM 5
+#define LPT_PR_NUM 5
+
+#define BXT_PR 0x84
+#define BXT_SSFSTS_CTL 0xa0
+#define BXT_FREG_NUM 12
+#define BXT_PR_NUM 6
+
+#define CNL_PR 0x84
+#define CNL_FREG_NUM 6
+#define CNL_PR_NUM 5
+
+#define LVSCC 0xc4
+#define UVSCC 0xc8
+#define ERASE_OPCODE_SHIFT 8
+#define ERASE_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT)
+#define ERASE_64K_OPCODE_SHIFT 16
+#define ERASE_64K_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT)
+
+#define INTEL_SPI_TIMEOUT 5000 /* ms */
+#define INTEL_SPI_FIFO_SZ 64
+
+/**
+ * struct intel_spi - Driver private data
+ * @dev: Device pointer
+ * @info: Pointer to board specific info
+ * @base: Beginning of MMIO space
+ * @pregs: Start of protection registers
+ * @sregs: Start of software sequencer registers
+ * @master: Pointer to the SPI controller structure
+ * @nregions: Maximum number of regions
+ * @pr_num: Maximum number of protected range registers
+ * @locked: Is SPI setting locked
+ * @swseq_reg: Use SW sequencer in register reads/writes
+ * @swseq_erase: Use SW sequencer in erase operation
+ * @atomic_preopcode: Holds preopcode when atomic sequence is requested
+ * @opcodes: Opcodes which are supported. This are programmed by BIOS
+ * before it locks down the controller.
+ * @mem_ops: Pointer to SPI MEM ops supported by the controller
+ */
+struct intel_spi {
+ struct device *dev;
+ const struct intel_spi_boardinfo *info;
+ void __iomem *base;
+ void __iomem *pregs;
+ void __iomem *sregs;
+ struct spi_controller *master;
+ size_t nregions;
+ size_t pr_num;
+ bool locked;
+ bool swseq_reg;
+ bool swseq_erase;
+ u8 atomic_preopcode;
+ u8 opcodes[8];
+ const struct intel_spi_mem_op *mem_ops;
+};
+
+struct intel_spi_mem_op {
+ struct spi_mem_op mem_op;
+ u32 replacement_op;
+ int (*exec_op)(struct intel_spi *ispi,
+ const struct intel_spi_mem_op *iop,
+ const struct spi_mem_op *op);
+};
+
+static bool writeable;
+module_param(writeable, bool, 0);
+MODULE_PARM_DESC(writeable, "Enable write access to SPI flash chip (default=0)");
+
+static void intel_spi_dump_regs(struct intel_spi *ispi)
+{
+ u32 value;
+ int i;
+
+ dev_dbg(ispi->dev, "BFPREG=0x%08x\n", readl(ispi->base + BFPREG));
+
+ value = readl(ispi->base + HSFSTS_CTL);
+ dev_dbg(ispi->dev, "HSFSTS_CTL=0x%08x\n", value);
+ if (value & HSFSTS_CTL_FLOCKDN)
+ dev_dbg(ispi->dev, "-> Locked\n");
+
+ dev_dbg(ispi->dev, "FADDR=0x%08x\n", readl(ispi->base + FADDR));
+ dev_dbg(ispi->dev, "DLOCK=0x%08x\n", readl(ispi->base + DLOCK));
+
+ for (i = 0; i < 16; i++)
+ dev_dbg(ispi->dev, "FDATA(%d)=0x%08x\n",
+ i, readl(ispi->base + FDATA(i)));
+
+ dev_dbg(ispi->dev, "FRACC=0x%08x\n", readl(ispi->base + FRACC));
+
+ for (i = 0; i < ispi->nregions; i++)
+ dev_dbg(ispi->dev, "FREG(%d)=0x%08x\n", i,
+ readl(ispi->base + FREG(i)));
+ for (i = 0; i < ispi->pr_num; i++)
+ dev_dbg(ispi->dev, "PR(%d)=0x%08x\n", i,
+ readl(ispi->pregs + PR(i)));
+
+ if (ispi->sregs) {
+ value = readl(ispi->sregs + SSFSTS_CTL);
+ dev_dbg(ispi->dev, "SSFSTS_CTL=0x%08x\n", value);
+ dev_dbg(ispi->dev, "PREOP_OPTYPE=0x%08x\n",
+ readl(ispi->sregs + PREOP_OPTYPE));
+ dev_dbg(ispi->dev, "OPMENU0=0x%08x\n",
+ readl(ispi->sregs + OPMENU0));
+ dev_dbg(ispi->dev, "OPMENU1=0x%08x\n",
+ readl(ispi->sregs + OPMENU1));
+ }
+
+ dev_dbg(ispi->dev, "LVSCC=0x%08x\n", readl(ispi->base + LVSCC));
+ dev_dbg(ispi->dev, "UVSCC=0x%08x\n", readl(ispi->base + UVSCC));
+
+ dev_dbg(ispi->dev, "Protected regions:\n");
+ for (i = 0; i < ispi->pr_num; i++) {
+ u32 base, limit;
+
+ value = readl(ispi->pregs + PR(i));
+ if (!(value & (PR_WPE | PR_RPE)))
+ continue;
+
+ limit = (value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT;
+ base = value & PR_BASE_MASK;
+
+ dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x [%c%c]\n",
+ i, base << 12, (limit << 12) | 0xfff,
+ value & PR_WPE ? 'W' : '.', value & PR_RPE ? 'R' : '.');
+ }
+
+ dev_dbg(ispi->dev, "Flash regions:\n");
+ for (i = 0; i < ispi->nregions; i++) {
+ u32 region, base, limit;
+
+ region = readl(ispi->base + FREG(i));
+ base = region & FREG_BASE_MASK;
+ limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT;
+
+ if (base >= limit || (i > 0 && limit == 0))
+ dev_dbg(ispi->dev, " %02d disabled\n", i);
+ else
+ dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x\n",
+ i, base << 12, (limit << 12) | 0xfff);
+ }
+
+ dev_dbg(ispi->dev, "Using %cW sequencer for register access\n",
+ ispi->swseq_reg ? 'S' : 'H');
+ dev_dbg(ispi->dev, "Using %cW sequencer for erase operation\n",
+ ispi->swseq_erase ? 'S' : 'H');
+}
+
+/* Reads max INTEL_SPI_FIFO_SZ bytes from the device fifo */
+static int intel_spi_read_block(struct intel_spi *ispi, void *buf, size_t size)
+{
+ size_t bytes;
+ int i = 0;
+
+ if (size > INTEL_SPI_FIFO_SZ)
+ return -EINVAL;
+
+ while (size > 0) {
+ bytes = min_t(size_t, size, 4);
+ memcpy_fromio(buf, ispi->base + FDATA(i), bytes);
+ size -= bytes;
+ buf += bytes;
+ i++;
+ }
+
+ return 0;
+}
+
+/* Writes max INTEL_SPI_FIFO_SZ bytes to the device fifo */
+static int intel_spi_write_block(struct intel_spi *ispi, const void *buf,
+ size_t size)
+{
+ size_t bytes;
+ int i = 0;
+
+ if (size > INTEL_SPI_FIFO_SZ)
+ return -EINVAL;
+
+ while (size > 0) {
+ bytes = min_t(size_t, size, 4);
+ memcpy_toio(ispi->base + FDATA(i), buf, bytes);
+ size -= bytes;
+ buf += bytes;
+ i++;
+ }
+
+ return 0;
+}
+
+static int intel_spi_wait_hw_busy(struct intel_spi *ispi)
+{
+ u32 val;
+
+ return readl_poll_timeout(ispi->base + HSFSTS_CTL, val,
+ !(val & HSFSTS_CTL_SCIP), 0,
+ INTEL_SPI_TIMEOUT * 1000);
+}
+
+static int intel_spi_wait_sw_busy(struct intel_spi *ispi)
+{
+ u32 val;
+
+ return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val,
+ !(val & SSFSTS_CTL_SCIP), 0,
+ INTEL_SPI_TIMEOUT * 1000);
+}
+
+static bool intel_spi_set_writeable(struct intel_spi *ispi)
+{
+ if (!ispi->info->set_writeable)
+ return false;
+
+ return ispi->info->set_writeable(ispi->base, ispi->info->data);
+}
+
+static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode, int optype)
+{
+ int i;
+ int preop;
+
+ if (ispi->locked) {
+ for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++)
+ if (ispi->opcodes[i] == opcode)
+ return i;
+
+ return -EINVAL;
+ }
+
+ /* The lock is off, so just use index 0 */
+ writel(opcode, ispi->sregs + OPMENU0);
+ preop = readw(ispi->sregs + PREOP_OPTYPE);
+ writel(optype << 16 | preop, ispi->sregs + PREOP_OPTYPE);
+
+ return 0;
+}
+
+static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, size_t len)
+{
+ u32 val, status;
+ int ret;
+
+ val = readl(ispi->base + HSFSTS_CTL);
+ val &= ~(HSFSTS_CTL_FCYCLE_MASK | HSFSTS_CTL_FDBC_MASK);
+
+ switch (opcode) {
+ case SPINOR_OP_RDID:
+ val |= HSFSTS_CTL_FCYCLE_RDID;
+ break;
+ case SPINOR_OP_WRSR:
+ val |= HSFSTS_CTL_FCYCLE_WRSR;
+ break;
+ case SPINOR_OP_RDSR:
+ val |= HSFSTS_CTL_FCYCLE_RDSR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (len > INTEL_SPI_FIFO_SZ)
+ return -EINVAL;
+
+ val |= (len - 1) << HSFSTS_CTL_FDBC_SHIFT;
+ val |= HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
+ val |= HSFSTS_CTL_FGO;
+ writel(val, ispi->base + HSFSTS_CTL);
+
+ ret = intel_spi_wait_hw_busy(ispi);
+ if (ret)
+ return ret;
+
+ status = readl(ispi->base + HSFSTS_CTL);
+ if (status & HSFSTS_CTL_FCERR)
+ return -EIO;
+ else if (status & HSFSTS_CTL_AEL)
+ return -EACCES;
+
+ return 0;
+}
+
+static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len,
+ int optype)
+{
+ u32 val = 0, status;
+ u8 atomic_preopcode;
+ int ret;
+
+ ret = intel_spi_opcode_index(ispi, opcode, optype);
+ if (ret < 0)
+ return ret;
+
+ if (len > INTEL_SPI_FIFO_SZ)
+ return -EINVAL;
+
+ /*
+ * Always clear it after each SW sequencer operation regardless
+ * of whether it is successful or not.
+ */
+ atomic_preopcode = ispi->atomic_preopcode;
+ ispi->atomic_preopcode = 0;
+
+ /* Only mark 'Data Cycle' bit when there is data to be transferred */
+ if (len > 0)
+ val = ((len - 1) << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS;
+ val |= ret << SSFSTS_CTL_COP_SHIFT;
+ val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE;
+ val |= SSFSTS_CTL_SCGO;
+ if (atomic_preopcode) {
+ u16 preop;
+
+ switch (optype) {
+ case OPTYPE_WRITE_NO_ADDR:
+ case OPTYPE_WRITE_WITH_ADDR:
+ /* Pick matching preopcode for the atomic sequence */
+ preop = readw(ispi->sregs + PREOP_OPTYPE);
+ if ((preop & 0xff) == atomic_preopcode)
+ ; /* Do nothing */
+ else if ((preop >> 8) == atomic_preopcode)
+ val |= SSFSTS_CTL_SPOP;
+ else
+ return -EINVAL;
+
+ /* Enable atomic sequence */
+ val |= SSFSTS_CTL_ACS;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+ writel(val, ispi->sregs + SSFSTS_CTL);
+
+ ret = intel_spi_wait_sw_busy(ispi);
+ if (ret)
+ return ret;
+
+ status = readl(ispi->sregs + SSFSTS_CTL);
+ if (status & SSFSTS_CTL_FCERR)
+ return -EIO;
+ else if (status & SSFSTS_CTL_AEL)
+ return -EACCES;
+
+ return 0;
+}
+
+static int intel_spi_read_reg(struct intel_spi *ispi,
+ const struct intel_spi_mem_op *iop,
+ const struct spi_mem_op *op)
+{
+ size_t nbytes = op->data.nbytes;
+ u8 opcode = op->cmd.opcode;
+ int ret;
+
+ /* Address of the first chip */
+ writel(0, ispi->base + FADDR);
+
+ if (ispi->swseq_reg)
+ ret = intel_spi_sw_cycle(ispi, opcode, nbytes,
+ OPTYPE_READ_NO_ADDR);
+ else
+ ret = intel_spi_hw_cycle(ispi, opcode, nbytes);
+
+ if (ret)
+ return ret;
+
+ return intel_spi_read_block(ispi, op->data.buf.in, nbytes);
+}
+
+static int intel_spi_write_reg(struct intel_spi *ispi,
+ const struct intel_spi_mem_op *iop,
+ const struct spi_mem_op *op)
+{
+ size_t nbytes = op->data.nbytes;
+ u8 opcode = op->cmd.opcode;
+ int ret;
+
+ /*
+ * This is handled with atomic operation and preop code in Intel
+ * controller so we only verify that it is available. If the
+ * controller is not locked, program the opcode to the PREOP
+ * register for later use.
+ *
+ * When hardware sequencer is used there is no need to program
+ * any opcodes (it handles them automatically as part of a command).
+ */
+ if (opcode == SPINOR_OP_WREN) {
+ u16 preop;
+
+ if (!ispi->swseq_reg)
+ return 0;
+
+ preop = readw(ispi->sregs + PREOP_OPTYPE);
+ if ((preop & 0xff) != opcode && (preop >> 8) != opcode) {
+ if (ispi->locked)
+ return -EINVAL;
+ writel(opcode, ispi->sregs + PREOP_OPTYPE);
+ }
+
+ /*
+ * This enables atomic sequence on next SW sycle. Will
+ * be cleared after next operation.
+ */
+ ispi->atomic_preopcode = opcode;
+ return 0;
+ }
+
+ /*
+ * We hope that HW sequencer will do the right thing automatically and
+ * with the SW sequencer we cannot use preopcode anyway, so just ignore
+ * the Write Disable operation and pretend it was completed
+ * successfully.
+ */
+ if (opcode == SPINOR_OP_WRDI)
+ return 0;
+
+ writel(0, ispi->base + FADDR);
+
+ /* Write the value beforehand */
+ ret = intel_spi_write_block(ispi, op->data.buf.out, nbytes);
+ if (ret)
+ return ret;
+
+ if (ispi->swseq_reg)
+ return intel_spi_sw_cycle(ispi, opcode, nbytes,
+ OPTYPE_WRITE_NO_ADDR);
+ return intel_spi_hw_cycle(ispi, opcode, nbytes);
+}
+
+static int intel_spi_read(struct intel_spi *ispi,
+ const struct intel_spi_mem_op *iop,
+ const struct spi_mem_op *op)
+{
+ void *read_buf = op->data.buf.in;
+ size_t block_size, nbytes = op->data.nbytes;
+ u32 addr = op->addr.val;
+ u32 val, status;
+ int ret;
+
+ /*
+ * Atomic sequence is not expected with HW sequencer reads. Make
+ * sure it is cleared regardless.
+ */
+ if (WARN_ON_ONCE(ispi->atomic_preopcode))
+ ispi->atomic_preopcode = 0;
+
+ while (nbytes > 0) {
+ block_size = min_t(size_t, nbytes, INTEL_SPI_FIFO_SZ);
+
+ /* Read cannot cross 4K boundary */
+ block_size = min_t(loff_t, addr + block_size,
+ round_up(addr + 1, SZ_4K)) - addr;
+
+ writel(addr, ispi->base + FADDR);
+
+ val = readl(ispi->base + HSFSTS_CTL);
+ val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
+ val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
+ val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT;
+ val |= HSFSTS_CTL_FCYCLE_READ;
+ val |= HSFSTS_CTL_FGO;
+ writel(val, ispi->base + HSFSTS_CTL);
+
+ ret = intel_spi_wait_hw_busy(ispi);
+ if (ret)
+ return ret;
+
+ status = readl(ispi->base + HSFSTS_CTL);
+ if (status & HSFSTS_CTL_FCERR)
+ ret = -EIO;
+ else if (status & HSFSTS_CTL_AEL)
+ ret = -EACCES;
+
+ if (ret < 0) {
+ dev_err(ispi->dev, "read error: %x: %#x\n", addr, status);
+ return ret;
+ }
+
+ ret = intel_spi_read_block(ispi, read_buf, block_size);
+ if (ret)
+ return ret;
+
+ nbytes -= block_size;
+ addr += block_size;
+ read_buf += block_size;
+ }
+
+ return 0;
+}
+
+static int intel_spi_write(struct intel_spi *ispi,
+ const struct intel_spi_mem_op *iop,
+ const struct spi_mem_op *op)
+{
+ size_t block_size, nbytes = op->data.nbytes;
+ const void *write_buf = op->data.buf.out;
+ u32 addr = op->addr.val;
+ u32 val, status;
+ int ret;
+
+ /* Not needed with HW sequencer write, make sure it is cleared */
+ ispi->atomic_preopcode = 0;
+
+ while (nbytes > 0) {
+ block_size = min_t(size_t, nbytes, INTEL_SPI_FIFO_SZ);
+
+ /* Write cannot cross 4K boundary */
+ block_size = min_t(loff_t, addr + block_size,
+ round_up(addr + 1, SZ_4K)) - addr;
+
+ writel(addr, ispi->base + FADDR);
+
+ val = readl(ispi->base + HSFSTS_CTL);
+ val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
+ val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
+ val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT;
+ val |= HSFSTS_CTL_FCYCLE_WRITE;
+
+ ret = intel_spi_write_block(ispi, write_buf, block_size);
+ if (ret) {
+ dev_err(ispi->dev, "failed to write block\n");
+ return ret;
+ }
+
+ /* Start the write now */
+ val |= HSFSTS_CTL_FGO;
+ writel(val, ispi->base + HSFSTS_CTL);
+
+ ret = intel_spi_wait_hw_busy(ispi);
+ if (ret) {
+ dev_err(ispi->dev, "timeout\n");
+ return ret;
+ }
+
+ status = readl(ispi->base + HSFSTS_CTL);
+ if (status & HSFSTS_CTL_FCERR)
+ ret = -EIO;
+ else if (status & HSFSTS_CTL_AEL)
+ ret = -EACCES;
+
+ if (ret < 0) {
+ dev_err(ispi->dev, "write error: %x: %#x\n", addr, status);
+ return ret;
+ }
+
+ nbytes -= block_size;
+ addr += block_size;
+ write_buf += block_size;
+ }
+
+ return 0;
+}
+
+static int intel_spi_erase(struct intel_spi *ispi,
+ const struct intel_spi_mem_op *iop,
+ const struct spi_mem_op *op)
+{
+ u8 opcode = op->cmd.opcode;
+ u32 addr = op->addr.val;
+ u32 val, status;
+ int ret;
+
+ writel(addr, ispi->base + FADDR);
+
+ if (ispi->swseq_erase)
+ return intel_spi_sw_cycle(ispi, opcode, 0,
+ OPTYPE_WRITE_WITH_ADDR);
+
+ /* Not needed with HW sequencer erase, make sure it is cleared */
+ ispi->atomic_preopcode = 0;
+
+ val = readl(ispi->base + HSFSTS_CTL);
+ val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
+ val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
+ val |= HSFSTS_CTL_FGO;
+ val |= iop->replacement_op;
+ writel(val, ispi->base + HSFSTS_CTL);
+
+ ret = intel_spi_wait_hw_busy(ispi);
+ if (ret)
+ return ret;
+
+ status = readl(ispi->base + HSFSTS_CTL);
+ if (status & HSFSTS_CTL_FCERR)
+ return -EIO;
+ if (status & HSFSTS_CTL_AEL)
+ return -EACCES;
+
+ return 0;
+}
+
+static bool intel_spi_cmp_mem_op(const struct intel_spi_mem_op *iop,
+ const struct spi_mem_op *op)
+{
+ if (iop->mem_op.cmd.nbytes != op->cmd.nbytes ||
+ iop->mem_op.cmd.buswidth != op->cmd.buswidth ||
+ iop->mem_op.cmd.dtr != op->cmd.dtr ||
+ iop->mem_op.cmd.opcode != op->cmd.opcode)
+ return false;
+
+ if (iop->mem_op.addr.nbytes != op->addr.nbytes ||
+ iop->mem_op.addr.dtr != op->addr.dtr)
+ return false;
+
+ if (iop->mem_op.data.dir != op->data.dir ||
+ iop->mem_op.data.dtr != op->data.dtr)
+ return false;
+
+ if (iop->mem_op.data.dir != SPI_MEM_NO_DATA) {
+ if (iop->mem_op.data.buswidth != op->data.buswidth)
+ return false;
+ }
+
+ return true;
+}
+
+static const struct intel_spi_mem_op *
+intel_spi_match_mem_op(struct intel_spi *ispi, const struct spi_mem_op *op)
+{
+ const struct intel_spi_mem_op *iop;
+
+ for (iop = ispi->mem_ops; iop->mem_op.cmd.opcode; iop++) {
+ if (intel_spi_cmp_mem_op(iop, op))
+ break;
+ }
+
+ return iop->mem_op.cmd.opcode ? iop : NULL;
+}
+
+static bool intel_spi_supports_mem_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct intel_spi *ispi = spi_master_get_devdata(mem->spi->master);
+ const struct intel_spi_mem_op *iop;
+
+ iop = intel_spi_match_mem_op(ispi, op);
+ if (!iop) {
+ dev_dbg(ispi->dev, "%#x not supported\n", op->cmd.opcode);
+ return false;
+ }
+
+ /*
+ * For software sequencer check that the opcode is actually
+ * present in the opmenu if it is locked.
+ */
+ if (ispi->swseq_reg && ispi->locked) {
+ int i;
+
+ /* Check if it is in the locked opcodes list */
+ for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++) {
+ if (ispi->opcodes[i] == op->cmd.opcode)
+ return true;
+ }
+
+ dev_dbg(ispi->dev, "%#x not supported\n", op->cmd.opcode);
+ return false;
+ }
+
+ return true;
+}
+
+static int intel_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+ struct intel_spi *ispi = spi_master_get_devdata(mem->spi->master);
+ const struct intel_spi_mem_op *iop;
+
+ iop = intel_spi_match_mem_op(ispi, op);
+ if (!iop)
+ return -EOPNOTSUPP;
+
+ return iop->exec_op(ispi, iop, op);
+}
+
+static const char *intel_spi_get_name(struct spi_mem *mem)
+{
+ const struct intel_spi *ispi = spi_master_get_devdata(mem->spi->master);
+
+ /*
+ * Return name of the flash controller device to be compatible
+ * with the MTD version.
+ */
+ return dev_name(ispi->dev);
+}
+
+static const struct spi_controller_mem_ops intel_spi_mem_ops = {
+ .supports_op = intel_spi_supports_mem_op,
+ .exec_op = intel_spi_exec_mem_op,
+ .get_name = intel_spi_get_name,
+};
+
+#define INTEL_SPI_OP_ADDR(__nbytes) \
+ { \
+ .nbytes = __nbytes, \
+ }
+
+#define INTEL_SPI_OP_NO_DATA \
+ { \
+ .dir = SPI_MEM_NO_DATA, \
+ }
+
+#define INTEL_SPI_OP_DATA_IN(__buswidth) \
+ { \
+ .dir = SPI_MEM_DATA_IN, \
+ .buswidth = __buswidth, \
+ }
+
+#define INTEL_SPI_OP_DATA_OUT(__buswidth) \
+ { \
+ .dir = SPI_MEM_DATA_OUT, \
+ .buswidth = __buswidth, \
+ }
+
+#define INTEL_SPI_MEM_OP(__cmd, __addr, __data, __exec_op) \
+ { \
+ .mem_op = { \
+ .cmd = __cmd, \
+ .addr = __addr, \
+ .data = __data, \
+ }, \
+ .exec_op = __exec_op, \
+ }
+
+#define INTEL_SPI_MEM_OP_REPL(__cmd, __addr, __data, __exec_op, __repl) \
+ { \
+ .mem_op = { \
+ .cmd = __cmd, \
+ .addr = __addr, \
+ .data = __data, \
+ }, \
+ .exec_op = __exec_op, \
+ .replacement_op = __repl, \
+ }
+
+/*
+ * The controller handles pretty much everything internally based on the
+ * SFDP data but we want to make sure we only support the operations
+ * actually possible. Only check buswidth and transfer direction, the
+ * core validates data.
+ */
+#define INTEL_SPI_GENERIC_OPS \
+ /* Status register operations */ \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ INTEL_SPI_OP_DATA_IN(1), \
+ intel_spi_read_reg), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ INTEL_SPI_OP_DATA_IN(1), \
+ intel_spi_read_reg), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ INTEL_SPI_OP_DATA_OUT(1), \
+ intel_spi_write_reg), \
+ /* Normal read */ \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \
+ INTEL_SPI_OP_ADDR(3), \
+ INTEL_SPI_OP_DATA_IN(1), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \
+ INTEL_SPI_OP_ADDR(3), \
+ INTEL_SPI_OP_DATA_IN(2), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \
+ INTEL_SPI_OP_ADDR(3), \
+ INTEL_SPI_OP_DATA_IN(4), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(1), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(2), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(4), \
+ intel_spi_read), \
+ /* Fast read */ \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \
+ INTEL_SPI_OP_ADDR(3), \
+ INTEL_SPI_OP_DATA_IN(1), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \
+ INTEL_SPI_OP_ADDR(3), \
+ INTEL_SPI_OP_DATA_IN(2), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \
+ INTEL_SPI_OP_ADDR(3), \
+ INTEL_SPI_OP_DATA_IN(4), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(1), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(2), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(4), \
+ intel_spi_read), \
+ /* Read with 4-byte address opcode */ \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_4B, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(1), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_4B, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(2), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_4B, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(4), \
+ intel_spi_read), \
+ /* Fast read with 4-byte address opcode */ \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST_4B, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(1), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST_4B, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(2), \
+ intel_spi_read), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST_4B, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_IN(4), \
+ intel_spi_read), \
+ /* Write operations */ \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_PP, 1), \
+ INTEL_SPI_OP_ADDR(3), \
+ INTEL_SPI_OP_DATA_OUT(1), \
+ intel_spi_write), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_PP, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_OUT(1), \
+ intel_spi_write), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_PP_4B, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ INTEL_SPI_OP_DATA_OUT(1), \
+ intel_spi_write), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_NO_DATA, \
+ intel_spi_write_reg), \
+ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_NO_DATA, \
+ intel_spi_write_reg), \
+ /* Erase operations */ \
+ INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_BE_4K, 1), \
+ INTEL_SPI_OP_ADDR(3), \
+ SPI_MEM_OP_NO_DATA, \
+ intel_spi_erase, \
+ HSFSTS_CTL_FCYCLE_ERASE), \
+ INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_BE_4K, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ SPI_MEM_OP_NO_DATA, \
+ intel_spi_erase, \
+ HSFSTS_CTL_FCYCLE_ERASE), \
+ INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_BE_4K_4B, 1), \
+ INTEL_SPI_OP_ADDR(4), \
+ SPI_MEM_OP_NO_DATA, \
+ intel_spi_erase, \
+ HSFSTS_CTL_FCYCLE_ERASE) \
+
+static const struct intel_spi_mem_op generic_mem_ops[] = {
+ INTEL_SPI_GENERIC_OPS,
+ { },
+};
+
+static const struct intel_spi_mem_op erase_64k_mem_ops[] = {
+ INTEL_SPI_GENERIC_OPS,
+ /* 64k sector erase operations */
+ INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_SE, 1),
+ INTEL_SPI_OP_ADDR(3),
+ SPI_MEM_OP_NO_DATA,
+ intel_spi_erase,
+ HSFSTS_CTL_FCYCLE_ERASE_64K),
+ INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_SE, 1),
+ INTEL_SPI_OP_ADDR(4),
+ SPI_MEM_OP_NO_DATA,
+ intel_spi_erase,
+ HSFSTS_CTL_FCYCLE_ERASE_64K),
+ INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_SE_4B, 1),
+ INTEL_SPI_OP_ADDR(4),
+ SPI_MEM_OP_NO_DATA,
+ intel_spi_erase,
+ HSFSTS_CTL_FCYCLE_ERASE_64K),
+ { },
+};
+
+static int intel_spi_init(struct intel_spi *ispi)
+{
+ u32 opmenu0, opmenu1, lvscc, uvscc, val;
+ bool erase_64k = false;
+ int i;
+
+ switch (ispi->info->type) {
+ case INTEL_SPI_BYT:
+ ispi->sregs = ispi->base + BYT_SSFSTS_CTL;
+ ispi->pregs = ispi->base + BYT_PR;
+ ispi->nregions = BYT_FREG_NUM;
+ ispi->pr_num = BYT_PR_NUM;
+ ispi->swseq_reg = true;
+ break;
+
+ case INTEL_SPI_LPT:
+ ispi->sregs = ispi->base + LPT_SSFSTS_CTL;
+ ispi->pregs = ispi->base + LPT_PR;
+ ispi->nregions = LPT_FREG_NUM;
+ ispi->pr_num = LPT_PR_NUM;
+ ispi->swseq_reg = true;
+ break;
+
+ case INTEL_SPI_BXT:
+ ispi->sregs = ispi->base + BXT_SSFSTS_CTL;
+ ispi->pregs = ispi->base + BXT_PR;
+ ispi->nregions = BXT_FREG_NUM;
+ ispi->pr_num = BXT_PR_NUM;
+ erase_64k = true;
+ break;
+
+ case INTEL_SPI_CNL:
+ ispi->sregs = NULL;
+ ispi->pregs = ispi->base + CNL_PR;
+ ispi->nregions = CNL_FREG_NUM;
+ ispi->pr_num = CNL_PR_NUM;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Try to disable write protection if user asked to do so */
+ if (writeable && !intel_spi_set_writeable(ispi)) {
+ dev_warn(ispi->dev, "can't disable chip write protection\n");
+ writeable = false;
+ }
+
+ /* Disable #SMI generation from HW sequencer */
+ val = readl(ispi->base + HSFSTS_CTL);
+ val &= ~HSFSTS_CTL_FSMIE;
+ writel(val, ispi->base + HSFSTS_CTL);
+
+ /*
+ * Determine whether erase operation should use HW or SW sequencer.
+ *
+ * The HW sequencer has a predefined list of opcodes, with only the
+ * erase opcode being programmable in LVSCC and UVSCC registers.
+ * If these registers don't contain a valid erase opcode, erase
+ * cannot be done using HW sequencer.
+ */
+ lvscc = readl(ispi->base + LVSCC);
+ uvscc = readl(ispi->base + UVSCC);
+ if (!(lvscc & ERASE_OPCODE_MASK) || !(uvscc & ERASE_OPCODE_MASK))
+ ispi->swseq_erase = true;
+ /* SPI controller on Intel BXT supports 64K erase opcode */
+ if (ispi->info->type == INTEL_SPI_BXT && !ispi->swseq_erase)
+ if (!(lvscc & ERASE_64K_OPCODE_MASK) ||
+ !(uvscc & ERASE_64K_OPCODE_MASK))
+ erase_64k = false;
+
+ if (!ispi->sregs && (ispi->swseq_reg || ispi->swseq_erase)) {
+ dev_err(ispi->dev, "software sequencer not supported, but required\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Some controllers can only do basic operations using hardware
+ * sequencer. All other operations are supposed to be carried out
+ * using software sequencer.
+ */
+ if (ispi->swseq_reg) {
+ /* Disable #SMI generation from SW sequencer */
+ val = readl(ispi->sregs + SSFSTS_CTL);
+ val &= ~SSFSTS_CTL_FSMIE;
+ writel(val, ispi->sregs + SSFSTS_CTL);
+ }
+
+ /* Check controller's lock status */
+ val = readl(ispi->base + HSFSTS_CTL);
+ ispi->locked = !!(val & HSFSTS_CTL_FLOCKDN);
+
+ if (ispi->locked && ispi->sregs) {
+ /*
+ * BIOS programs allowed opcodes and then locks down the
+ * register. So read back what opcodes it decided to support.
+ * That's the set we are going to support as well.
+ */
+ opmenu0 = readl(ispi->sregs + OPMENU0);
+ opmenu1 = readl(ispi->sregs + OPMENU1);
+
+ if (opmenu0 && opmenu1) {
+ for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) {
+ ispi->opcodes[i] = opmenu0 >> i * 8;
+ ispi->opcodes[i + 4] = opmenu1 >> i * 8;
+ }
+ }
+ }
+
+ if (erase_64k) {
+ dev_dbg(ispi->dev, "Using erase_64k memory operations");
+ ispi->mem_ops = erase_64k_mem_ops;
+ } else {
+ dev_dbg(ispi->dev, "Using generic memory operations");
+ ispi->mem_ops = generic_mem_ops;
+ }
+
+ intel_spi_dump_regs(ispi);
+ return 0;
+}
+
+static bool intel_spi_is_protected(const struct intel_spi *ispi,
+ unsigned int base, unsigned int limit)
+{
+ int i;
+
+ for (i = 0; i < ispi->pr_num; i++) {
+ u32 pr_base, pr_limit, pr_value;
+
+ pr_value = readl(ispi->pregs + PR(i));
+ if (!(pr_value & (PR_WPE | PR_RPE)))
+ continue;
+
+ pr_limit = (pr_value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT;
+ pr_base = pr_value & PR_BASE_MASK;
+
+ if (pr_base >= base && pr_limit <= limit)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * There will be a single partition holding all enabled flash regions. We
+ * call this "BIOS".
+ */
+static void intel_spi_fill_partition(struct intel_spi *ispi,
+ struct mtd_partition *part)
+{
+ u64 end;
+ int i;
+
+ memset(part, 0, sizeof(*part));
+
+ /* Start from the mandatory descriptor region */
+ part->size = 4096;
+ part->name = "BIOS";
+
+ /*
+ * Now try to find where this partition ends based on the flash
+ * region registers.
+ */
+ for (i = 1; i < ispi->nregions; i++) {
+ u32 region, base, limit;
+
+ region = readl(ispi->base + FREG(i));
+ base = region & FREG_BASE_MASK;
+ limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT;
+
+ if (base >= limit || limit == 0)
+ continue;
+
+ /*
+ * If any of the regions have protection bits set, make the
+ * whole partition read-only to be on the safe side.
+ *
+ * Also if the user did not ask the chip to be writeable
+ * mask the bit too.
+ */
+ if (!writeable || intel_spi_is_protected(ispi, base, limit))
+ part->mask_flags |= MTD_WRITEABLE;
+
+ end = (limit << 12) + 4096;
+ if (end > part->size)
+ part->size = end;
+ }
+}
+
+static int intel_spi_populate_chip(struct intel_spi *ispi)
+{
+ struct flash_platform_data *pdata;
+ struct spi_board_info chip;
+
+ pdata = devm_kzalloc(ispi->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->nr_parts = 1;
+ pdata->parts = devm_kcalloc(ispi->dev, sizeof(*pdata->parts),
+ pdata->nr_parts, GFP_KERNEL);
+ if (!pdata->parts)
+ return -ENOMEM;
+
+ intel_spi_fill_partition(ispi, pdata->parts);
+
+ memset(&chip, 0, sizeof(chip));
+ snprintf(chip.modalias, 8, "spi-nor");
+ chip.platform_data = pdata;
+
+ return spi_new_device(ispi->master, &chip) ? 0 : -ENODEV;
+}
+
+/**
+ * intel_spi_probe() - Probe the Intel SPI flash controller
+ * @dev: Pointer to the parent device
+ * @mem: MMIO resource
+ * @info: Platform spefific information
+ *
+ * Probes Intel SPI flash controller and creates the flash chip device.
+ * Returns %0 on success and negative errno in case of failure.
+ */
+int intel_spi_probe(struct device *dev, struct resource *mem,
+ const struct intel_spi_boardinfo *info)
+{
+ struct spi_controller *master;
+ struct intel_spi *ispi;
+ int ret;
+
+ master = devm_spi_alloc_master(dev, sizeof(*ispi));
+ if (!master)
+ return -ENOMEM;
+
+ master->mem_ops = &intel_spi_mem_ops;
+
+ ispi = spi_master_get_devdata(master);
+
+ ispi->base = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(ispi->base))
+ return PTR_ERR(ispi->base);
+
+ ispi->dev = dev;
+ ispi->master = master;
+ ispi->info = info;
+
+ ret = intel_spi_init(ispi);
+ if (ret)
+ return ret;
+
+ ret = devm_spi_register_master(dev, master);
+ if (ret)
+ return ret;
+
+ return intel_spi_populate_chip(ispi);
+}
+EXPORT_SYMBOL_GPL(intel_spi_probe);
+
+MODULE_DESCRIPTION("Intel PCH/PCU SPI flash core driver");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-intel.h b/drivers/spi/spi-intel.h
new file mode 100644
index 000000000000..a4f0327a46ff
--- /dev/null
+++ b/drivers/spi/spi-intel.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Intel PCH/PCU SPI flash driver.
+ *
+ * Copyright (C) 2016 - 2022, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#ifndef SPI_INTEL_H
+#define SPI_INTEL_H
+
+#include <linux/platform_data/x86/spi-intel.h>
+
+struct resource;
+
+int intel_spi_probe(struct device *dev, struct resource *mem,
+ const struct intel_spi_boardinfo *info);
+
+#endif /* SPI_INTEL_H */
diff --git a/drivers/spi/spi-lantiq-ssc.c b/drivers/spi/spi-lantiq-ssc.c
index bcb52601804a..aae26f62ea87 100644
--- a/drivers/spi/spi-lantiq-ssc.c
+++ b/drivers/spi/spi-lantiq-ssc.c
@@ -906,17 +906,11 @@ static int lantiq_ssc_probe(struct platform_device *pdev)
struct spi_master *master;
struct lantiq_ssc_spi *spi;
const struct lantiq_ssc_hwcfg *hwcfg;
- const struct of_device_id *match;
u32 id, supports_dma, revision;
unsigned int num_cs;
int err;
- match = of_match_device(lantiq_ssc_match, dev);
- if (!match) {
- dev_err(dev, "no device match\n");
- return -EINVAL;
- }
- hwcfg = match->data;
+ hwcfg = of_device_get_match_data(dev);
master = spi_alloc_master(dev, sizeof(struct lantiq_ssc_spi));
if (!master)
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 37f4443ce9a0..e9d83d65873b 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -854,15 +854,13 @@ static int spi_mem_probe(struct spi_device *spi)
return memdrv->probe(mem);
}
-static int spi_mem_remove(struct spi_device *spi)
+static void spi_mem_remove(struct spi_device *spi)
{
struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
struct spi_mem *mem = spi_get_drvdata(spi);
if (memdrv->remove)
- return memdrv->remove(mem);
-
- return 0;
+ memdrv->remove(mem);
}
static void spi_mem_shutdown(struct spi_device *spi)
diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c
index 78a9bca8cc68..03630359ce70 100644
--- a/drivers/spi/spi-mpc512x-psc.c
+++ b/drivers/spi/spi-mpc512x-psc.c
@@ -23,7 +23,6 @@
#include <linux/clk.h>
#include <linux/spi/spi.h>
#include <linux/fsl_devices.h>
-#include <linux/gpio.h>
#include <asm/mpc52xx_psc.h>
enum {
@@ -128,17 +127,28 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi)
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))
- mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 1 : 0);
+ if (spi->cs_gpiod) {
+ if (mps->cs_control)
+ /* boardfile override */
+ mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 1 : 0);
+ else
+ /* gpiolib will deal with the inversion */
+ gpiod_set_value(spi->cs_gpiod, 1);
+ }
}
static void mpc512x_psc_spi_deactivate_cs(struct spi_device *spi)
{
struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master);
- if (mps->cs_control && gpio_is_valid(spi->cs_gpio))
- mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 0 : 1);
-
+ if (spi->cs_gpiod) {
+ if (mps->cs_control)
+ /* boardfile override */
+ mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 0 : 1);
+ else
+ /* gpiolib will deal with the inversion */
+ gpiod_set_value(spi->cs_gpiod, 0);
+ }
}
/* extract and scale size field in txsz or rxsz */
@@ -363,7 +373,6 @@ static int mpc512x_psc_spi_unprep_xfer_hw(struct spi_master *master)
static int mpc512x_psc_spi_setup(struct spi_device *spi)
{
struct mpc512x_psc_spi_cs *cs = spi->controller_state;
- int ret;
if (spi->bits_per_word % 8)
return -EINVAL;
@@ -373,18 +382,6 @@ static int mpc512x_psc_spi_setup(struct spi_device *spi)
if (!cs)
return -ENOMEM;
- if (gpio_is_valid(spi->cs_gpio)) {
- ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev));
- if (ret) {
- dev_err(&spi->dev, "can't get CS gpio: %d\n",
- ret);
- kfree(cs);
- return ret;
- }
- gpio_direction_output(spi->cs_gpio,
- spi->mode & SPI_CS_HIGH ? 0 : 1);
- }
-
spi->controller_state = cs;
}
@@ -396,8 +393,6 @@ static int mpc512x_psc_spi_setup(struct spi_device *spi)
static void mpc512x_psc_spi_cleanup(struct spi_device *spi)
{
- if (gpio_is_valid(spi->cs_gpio))
- gpio_free(spi->cs_gpio);
kfree(spi->controller_state);
}
@@ -476,11 +471,6 @@ static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id)
return IRQ_NONE;
}
-static void mpc512x_spi_cs_control(struct spi_device *spi, bool onoff)
-{
- gpio_set_value(spi->cs_gpio, onoff);
-}
-
static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
u32 size, unsigned int irq)
{
@@ -500,9 +490,7 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
mps->type = (int)of_device_get_match_data(dev);
mps->irq = irq;
- if (pdata == NULL) {
- mps->cs_control = mpc512x_spi_cs_control;
- } else {
+ if (pdata) {
mps->cs_control = pdata->cs_control;
master->bus_num = pdata->bus_num;
master->num_chipselect = pdata->max_chipselect;
@@ -513,6 +501,7 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
master->prepare_transfer_hardware = mpc512x_psc_spi_prep_xfer_hw;
master->transfer_one_message = mpc512x_psc_spi_msg_xfer;
master->unprepare_transfer_hardware = mpc512x_psc_spi_unprep_xfer_hw;
+ master->use_gpio_descriptors = true;
master->cleanup = mpc512x_psc_spi_cleanup;
master->dev.of_node = dev->of_node;
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
index 753bd313e6fd..1a0b3208dfca 100644
--- a/drivers/spi/spi-mt65xx.c
+++ b/drivers/spi/spi-mt65xx.c
@@ -12,7 +12,7 @@
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/platform_data/spi-mt65xx.h>
#include <linux/pm_runtime.h>
@@ -31,6 +31,7 @@
#define SPI_CFG2_REG 0x0028
#define SPI_TX_SRC_REG_64 0x002c
#define SPI_RX_DST_REG_64 0x0030
+#define SPI_CFG3_IPM_REG 0x0040
#define SPI_CFG0_SCK_HIGH_OFFSET 0
#define SPI_CFG0_SCK_LOW_OFFSET 8
@@ -43,11 +44,15 @@
#define SPI_CFG1_PACKET_LOOP_OFFSET 8
#define SPI_CFG1_PACKET_LENGTH_OFFSET 16
#define SPI_CFG1_GET_TICK_DLY_OFFSET 29
+#define SPI_CFG1_GET_TICK_DLY_OFFSET_V1 30
#define SPI_CFG1_GET_TICK_DLY_MASK 0xe0000000
+#define SPI_CFG1_GET_TICK_DLY_MASK_V1 0xc0000000
+
#define SPI_CFG1_CS_IDLE_MASK 0xff
#define SPI_CFG1_PACKET_LOOP_MASK 0xff00
#define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000
+#define SPI_CFG1_IPM_PACKET_LENGTH_MASK GENMASK(31, 16)
#define SPI_CFG2_SCK_HIGH_OFFSET 0
#define SPI_CFG2_SCK_LOW_OFFSET 16
@@ -68,7 +73,13 @@
#define SPI_CMD_TX_ENDIAN BIT(15)
#define SPI_CMD_FINISH_IE BIT(16)
#define SPI_CMD_PAUSE_IE BIT(17)
+#define SPI_CMD_IPM_NONIDLE_MODE BIT(19)
+#define SPI_CMD_IPM_SPIM_LOOP BIT(21)
+#define SPI_CMD_IPM_GET_TICKDLY_OFFSET 22
+#define SPI_CMD_IPM_GET_TICKDLY_MASK GENMASK(24, 22)
+#define SPI_CFG3_IPM_HALF_DUPLEX_DIR BIT(2)
+#define SPI_CFG3_IPM_HALF_DUPLEX_EN BIT(3)
#define MT8173_SPI_MAX_PAD_SEL 3
#define MTK_SPI_PAUSE_INT_STATUS 0x2
@@ -78,6 +89,7 @@
#define MTK_SPI_MAX_FIFO_SIZE 32U
#define MTK_SPI_PACKET_SIZE 1024
+#define MTK_SPI_IPM_PACKET_SIZE SZ_64K
#define MTK_SPI_32BITS_MASK (0xffffffff)
#define DMA_ADDR_EXT_BITS (36)
@@ -93,6 +105,9 @@ struct mtk_spi_compatible {
bool dma_ext;
/* some IC no need unprepare SPI clk */
bool no_need_unprepare;
+ /* IPM design adjust and extend register to support more features */
+ bool ipm_design;
+
};
struct mtk_spi {
@@ -116,6 +131,12 @@ static const struct mtk_spi_compatible mt2712_compat = {
.must_tx = true,
};
+static const struct mtk_spi_compatible mtk_ipm_compat = {
+ .enhance_timing = true,
+ .dma_ext = true,
+ .ipm_design = true,
+};
+
static const struct mtk_spi_compatible mt6765_compat = {
.need_pad_sel = true,
.must_tx = true,
@@ -157,6 +178,9 @@ static const struct mtk_chip_config mtk_default_chip_info = {
};
static const struct of_device_id mtk_spi_of_match[] = {
+ { .compatible = "mediatek,spi-ipm",
+ .data = (void *)&mtk_ipm_compat,
+ },
{ .compatible = "mediatek,mt2701-spi",
.data = (void *)&mtk_common_compat,
},
@@ -275,12 +299,11 @@ static int mtk_spi_set_hw_cs_timing(struct spi_device *spi)
return 0;
}
-static int mtk_spi_prepare_message(struct spi_master *master,
- struct spi_message *msg)
+static int mtk_spi_hw_init(struct spi_master *master,
+ struct spi_device *spi)
{
u16 cpha, cpol;
u32 reg_val;
- struct spi_device *spi = msg->spi;
struct mtk_chip_config *chip_config = spi->controller_data;
struct mtk_spi *mdata = spi_master_get_devdata(master);
@@ -288,6 +311,15 @@ static int mtk_spi_prepare_message(struct spi_master *master,
cpol = spi->mode & SPI_CPOL ? 1 : 0;
reg_val = readl(mdata->base + SPI_CMD_REG);
+ if (mdata->dev_comp->ipm_design) {
+ /* SPI transfer without idle time until packet length done */
+ reg_val |= SPI_CMD_IPM_NONIDLE_MODE;
+ if (spi->mode & SPI_LOOP)
+ reg_val |= SPI_CMD_IPM_SPIM_LOOP;
+ else
+ reg_val &= ~SPI_CMD_IPM_SPIM_LOOP;
+ }
+
if (cpha)
reg_val |= SPI_CMD_CPHA;
else
@@ -345,17 +377,39 @@ static int mtk_spi_prepare_message(struct spi_master *master,
mdata->base + SPI_PAD_SEL_REG);
/* tick delay */
- reg_val = readl(mdata->base + SPI_CFG1_REG);
- reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK;
- reg_val |= ((chip_config->tick_delay & 0x7)
- << SPI_CFG1_GET_TICK_DLY_OFFSET);
- writel(reg_val, mdata->base + SPI_CFG1_REG);
+ if (mdata->dev_comp->enhance_timing) {
+ if (mdata->dev_comp->ipm_design) {
+ reg_val = readl(mdata->base + SPI_CMD_REG);
+ reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK;
+ reg_val |= ((chip_config->tick_delay & 0x7)
+ << SPI_CMD_IPM_GET_TICKDLY_OFFSET);
+ writel(reg_val, mdata->base + SPI_CMD_REG);
+ } else {
+ reg_val = readl(mdata->base + SPI_CFG1_REG);
+ reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK;
+ reg_val |= ((chip_config->tick_delay & 0x7)
+ << SPI_CFG1_GET_TICK_DLY_OFFSET);
+ writel(reg_val, mdata->base + SPI_CFG1_REG);
+ }
+ } else {
+ reg_val = readl(mdata->base + SPI_CFG1_REG);
+ reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK_V1;
+ reg_val |= ((chip_config->tick_delay & 0x3)
+ << SPI_CFG1_GET_TICK_DLY_OFFSET_V1);
+ writel(reg_val, mdata->base + SPI_CFG1_REG);
+ }
/* set hw cs timing */
mtk_spi_set_hw_cs_timing(spi);
return 0;
}
+static int mtk_spi_prepare_message(struct spi_master *master,
+ struct spi_message *msg)
+{
+ return mtk_spi_hw_init(master, msg->spi);
+}
+
static void mtk_spi_set_cs(struct spi_device *spi, bool enable)
{
u32 reg_val;
@@ -377,13 +431,13 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable)
}
static void mtk_spi_prepare_transfer(struct spi_master *master,
- struct spi_transfer *xfer)
+ u32 speed_hz)
{
u32 div, sck_time, reg_val;
struct mtk_spi *mdata = spi_master_get_devdata(master);
- if (xfer->speed_hz < mdata->spi_clk_hz / 2)
- div = DIV_ROUND_UP(mdata->spi_clk_hz, xfer->speed_hz);
+ if (speed_hz < mdata->spi_clk_hz / 2)
+ div = DIV_ROUND_UP(mdata->spi_clk_hz, speed_hz);
else
div = 1;
@@ -414,12 +468,24 @@ 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);
+ if (mdata->dev_comp->ipm_design)
+ packet_size = min_t(u32,
+ mdata->xfer_len,
+ MTK_SPI_IPM_PACKET_SIZE);
+ else
+ 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);
+ if (mdata->dev_comp->ipm_design)
+ reg_val &= ~SPI_CFG1_IPM_PACKET_LENGTH_MASK;
+ else
+ reg_val &= ~SPI_CFG1_PACKET_LENGTH_MASK;
reg_val |= (packet_size - 1) << SPI_CFG1_PACKET_LENGTH_OFFSET;
+ reg_val &= ~SPI_CFG1_PACKET_LOOP_MASK;
reg_val |= (packet_loop - 1) << SPI_CFG1_PACKET_LOOP_OFFSET;
writel(reg_val, mdata->base + SPI_CFG1_REG);
}
@@ -514,7 +580,7 @@ static int mtk_spi_fifo_transfer(struct spi_master *master,
mdata->cur_transfer = xfer;
mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len);
mdata->num_xfered = 0;
- mtk_spi_prepare_transfer(master, xfer);
+ mtk_spi_prepare_transfer(master, xfer->speed_hz);
mtk_spi_setup_packet(master);
if (xfer->tx_buf) {
@@ -547,7 +613,7 @@ static int mtk_spi_dma_transfer(struct spi_master *master,
mdata->cur_transfer = xfer;
mdata->num_xfered = 0;
- mtk_spi_prepare_transfer(master, xfer);
+ mtk_spi_prepare_transfer(master, xfer->speed_hz);
cmd = readl(mdata->base + SPI_CMD_REG);
if (xfer->tx_buf)
@@ -582,6 +648,19 @@ static int mtk_spi_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer)
{
+ struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
+ u32 reg_val = 0;
+
+ /* prepare xfer direction and duplex mode */
+ if (mdata->dev_comp->ipm_design) {
+ if (!xfer->tx_buf || !xfer->rx_buf) {
+ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN;
+ if (xfer->rx_buf)
+ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR;
+ }
+ writel(reg_val, mdata->base + SPI_CFG3_IPM_REG);
+ }
+
if (master->can_dma(master, spi, xfer))
return mtk_spi_dma_transfer(master, spi, xfer);
else
@@ -605,8 +684,9 @@ static int mtk_spi_setup(struct spi_device *spi)
if (!spi->controller_data)
spi->controller_data = (void *)&mtk_default_chip_info;
- if (mdata->dev_comp->need_pad_sel && gpio_is_valid(spi->cs_gpio))
- gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
+ if (mdata->dev_comp->need_pad_sel && spi->cs_gpiod)
+ /* CS de-asserted, gpiolib will handle inversion */
+ gpiod_direction_output(spi->cs_gpiod, 0);
return 0;
}
@@ -730,6 +810,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
master->can_dma = mtk_spi_can_dma;
master->setup = mtk_spi_setup;
master->set_cs_timing = mtk_spi_set_hw_cs_timing;
+ master->use_gpio_descriptors = true;
of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node);
if (!of_id) {
@@ -746,6 +827,8 @@ static int mtk_spi_probe(struct platform_device *pdev)
if (mdata->dev_comp->must_tx)
master->flags = SPI_MASTER_MUST_TX;
+ if (mdata->dev_comp->ipm_design)
+ master->mode_bits |= SPI_LOOP;
if (mdata->dev_comp->need_pad_sel) {
mdata->pad_num = of_property_count_u32_elems(
@@ -853,25 +936,12 @@ static int mtk_spi_probe(struct platform_device *pdev)
goto err_disable_runtime_pm;
}
- if (!master->cs_gpios && master->num_chipselect > 1) {
+ if (!master->cs_gpiods && master->num_chipselect > 1) {
dev_err(&pdev->dev,
"cs_gpios not specified and num_chipselect > 1\n");
ret = -EINVAL;
goto err_disable_runtime_pm;
}
-
- if (master->cs_gpios) {
- for (i = 0; i < master->num_chipselect; i++) {
- ret = devm_gpio_request(&pdev->dev,
- master->cs_gpios[i],
- dev_name(&pdev->dev));
- if (ret) {
- dev_err(&pdev->dev,
- "can't get CS GPIO %i\n", i);
- goto err_disable_runtime_pm;
- }
- }
- }
}
if (mdata->dev_comp->dma_ext)
diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c
index 5c93730615f8..94fb09696677 100644
--- a/drivers/spi/spi-mtk-nor.c
+++ b/drivers/spi/spi-mtk-nor.c
@@ -95,6 +95,17 @@
#define CLK_TO_US(sp, clkcnt) DIV_ROUND_UP(clkcnt, sp->spi_freq / 1000000)
+struct mtk_nor_caps {
+ u8 dma_bits;
+
+ /* extra_dummy_bit is adding for the IP of new SoCs.
+ * Some new SoCs modify the timing of fetching registers' values
+ * and IDs of nor flash, they need a extra_dummy_bit which can add
+ * more clock cycles for fetching data.
+ */
+ u8 extra_dummy_bit;
+};
+
struct mtk_nor {
struct spi_controller *ctlr;
struct device *dev;
@@ -104,11 +115,13 @@ struct mtk_nor {
struct clk *spi_clk;
struct clk *ctlr_clk;
struct clk *axi_clk;
+ struct clk *axi_s_clk;
unsigned int spi_freq;
bool wbuf_en;
bool has_irq;
bool high_dma;
struct completion op_done;
+ const struct mtk_nor_caps *caps;
};
static inline void mtk_nor_rmw(struct mtk_nor *sp, u32 reg, u32 set, u32 clr)
@@ -554,7 +567,12 @@ static int mtk_nor_spi_mem_prg(struct mtk_nor *sp, const struct spi_mem_op *op)
}
// trigger op
- writel(prg_len * BITS_PER_BYTE, sp->base + MTK_NOR_REG_PRG_CNT);
+ if (rx_len)
+ writel(prg_len * BITS_PER_BYTE + sp->caps->extra_dummy_bit,
+ sp->base + MTK_NOR_REG_PRG_CNT);
+ else
+ writel(prg_len * BITS_PER_BYTE, sp->base + MTK_NOR_REG_PRG_CNT);
+
ret = mtk_nor_cmd_exec(sp, MTK_NOR_CMD_PROGRAM,
prg_len * BITS_PER_BYTE);
if (ret)
@@ -674,6 +692,7 @@ static void mtk_nor_disable_clk(struct mtk_nor *sp)
clk_disable_unprepare(sp->spi_clk);
clk_disable_unprepare(sp->ctlr_clk);
clk_disable_unprepare(sp->axi_clk);
+ clk_disable_unprepare(sp->axi_s_clk);
}
static int mtk_nor_enable_clk(struct mtk_nor *sp)
@@ -697,6 +716,14 @@ static int mtk_nor_enable_clk(struct mtk_nor *sp)
return ret;
}
+ ret = clk_prepare_enable(sp->axi_s_clk);
+ if (ret) {
+ clk_disable_unprepare(sp->spi_clk);
+ clk_disable_unprepare(sp->ctlr_clk);
+ clk_disable_unprepare(sp->axi_clk);
+ return ret;
+ }
+
return 0;
}
@@ -743,9 +770,25 @@ static const struct spi_controller_mem_ops mtk_nor_mem_ops = {
.exec_op = mtk_nor_exec_op
};
+static const struct mtk_nor_caps mtk_nor_caps_mt8173 = {
+ .dma_bits = 32,
+ .extra_dummy_bit = 0,
+};
+
+static const struct mtk_nor_caps mtk_nor_caps_mt8186 = {
+ .dma_bits = 32,
+ .extra_dummy_bit = 1,
+};
+
+static const struct mtk_nor_caps mtk_nor_caps_mt8192 = {
+ .dma_bits = 36,
+ .extra_dummy_bit = 0,
+};
+
static const struct of_device_id mtk_nor_match[] = {
- { .compatible = "mediatek,mt8192-nor", .data = (void *)36 },
- { .compatible = "mediatek,mt8173-nor", .data = (void *)32 },
+ { .compatible = "mediatek,mt8173-nor", .data = &mtk_nor_caps_mt8173 },
+ { .compatible = "mediatek,mt8186-nor", .data = &mtk_nor_caps_mt8186 },
+ { .compatible = "mediatek,mt8192-nor", .data = &mtk_nor_caps_mt8192 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_nor_match);
@@ -754,10 +797,10 @@ static int mtk_nor_probe(struct platform_device *pdev)
{
struct spi_controller *ctlr;
struct mtk_nor *sp;
+ struct mtk_nor_caps *caps;
void __iomem *base;
- struct clk *spi_clk, *ctlr_clk, *axi_clk;
+ struct clk *spi_clk, *ctlr_clk, *axi_clk, *axi_s_clk;
int ret, irq;
- unsigned long dma_bits;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
@@ -775,10 +818,16 @@ static int mtk_nor_probe(struct platform_device *pdev)
if (IS_ERR(axi_clk))
return PTR_ERR(axi_clk);
- dma_bits = (unsigned long)of_device_get_match_data(&pdev->dev);
- if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(dma_bits))) {
- dev_err(&pdev->dev, "failed to set dma mask(%lu)\n", dma_bits);
- return -EINVAL;
+ axi_s_clk = devm_clk_get_optional(&pdev->dev, "axi_s");
+ if (IS_ERR(axi_s_clk))
+ return PTR_ERR(axi_s_clk);
+
+ caps = (struct mtk_nor_caps *)of_device_get_match_data(&pdev->dev);
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(caps->dma_bits));
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set dma mask(%u)\n", caps->dma_bits);
+ return ret;
}
ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*sp));
@@ -808,7 +857,9 @@ static int mtk_nor_probe(struct platform_device *pdev)
sp->spi_clk = spi_clk;
sp->ctlr_clk = ctlr_clk;
sp->axi_clk = axi_clk;
- sp->high_dma = (dma_bits > 32);
+ sp->axi_s_clk = axi_s_clk;
+ sp->caps = caps;
+ sp->high_dma = caps->dma_bits > 32;
sp->buffer = dmam_alloc_coherent(&pdev->dev,
MTK_NOR_BOUNCE_BUF_SIZE + MTK_NOR_DMA_ALIGN,
&sp->buffer_dma, GFP_KERNEL);
diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c
index b62471ab6d7f..ba67dbed9fb8 100644
--- a/drivers/spi/spi-npcm-fiu.c
+++ b/drivers/spi/spi-npcm-fiu.c
@@ -201,7 +201,7 @@ struct fiu_data {
int fiu_max;
};
-static const struct npcm_fiu_info npxm7xx_fiu_info[] = {
+static const struct npcm_fiu_info npcm7xx_fiu_info[] = {
{.name = "FIU0", .fiu_id = FIU0,
.max_map_size = MAP_SIZE_128MB, .max_cs = 2},
{.name = "FIU3", .fiu_id = FIU3,
@@ -209,8 +209,8 @@ static const struct npcm_fiu_info npxm7xx_fiu_info[] = {
{.name = "FIUX", .fiu_id = FIUX,
.max_map_size = MAP_SIZE_16MB, .max_cs = 2} };
-static const struct fiu_data npxm7xx_fiu_data = {
- .npcm_fiu_data_info = npxm7xx_fiu_info,
+static const struct fiu_data npcm7xx_fiu_data = {
+ .npcm_fiu_data_info = npcm7xx_fiu_info,
.fiu_max = 3,
};
@@ -664,14 +664,13 @@ static const struct spi_controller_mem_ops npcm_fiu_mem_ops = {
};
static const struct of_device_id npcm_fiu_dt_ids[] = {
- { .compatible = "nuvoton,npcm750-fiu", .data = &npxm7xx_fiu_data },
+ { .compatible = "nuvoton,npcm750-fiu", .data = &npcm7xx_fiu_data },
{ /* sentinel */ }
};
static int npcm_fiu_probe(struct platform_device *pdev)
{
const struct fiu_data *fiu_data_match;
- const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct spi_controller *ctrl;
struct npcm_fiu_spi *fiu;
@@ -685,13 +684,12 @@ static int npcm_fiu_probe(struct platform_device *pdev)
fiu = spi_controller_get_devdata(ctrl);
- match = of_match_device(npcm_fiu_dt_ids, dev);
- if (!match || !match->data) {
+ fiu_data_match = of_device_get_match_data(dev);
+ if (!fiu_data_match) {
dev_err(dev, "No compatible OF match\n");
return -ENODEV;
}
- fiu_data_match = match->data;
id = of_alias_get_id(dev->of_node, "fiu");
if (id < 0 || id >= fiu_data_match->fiu_max) {
dev_err(dev, "Invalid platform device id: %d\n", id);
diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c
index f86433b29260..7e5c09a7d489 100644
--- a/drivers/spi/spi-pic32.c
+++ b/drivers/spi/spi-pic32.c
@@ -591,18 +591,16 @@ static int pic32_spi_setup(struct spi_device *spi)
* unreliable/erroneous SPI transactions.
* To avoid that we will always handle /CS by toggling GPIO.
*/
- if (!gpio_is_valid(spi->cs_gpio))
+ if (!spi->cs_gpiod)
return -EINVAL;
- gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
-
return 0;
}
static void pic32_spi_cleanup(struct spi_device *spi)
{
- /* de-activate cs-gpio */
- gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
+ /* de-activate cs-gpio, gpiolib will handle inversion */
+ gpiod_direction_output(spi->cs_gpiod, 0);
}
static int pic32_spi_dma_prep(struct pic32_spi *pic32s, struct device *dev)
@@ -784,6 +782,7 @@ static int pic32_spi_probe(struct platform_device *pdev)
master->unprepare_message = pic32_spi_unprepare_message;
master->prepare_transfer_hardware = pic32_spi_prepare_hardware;
master->unprepare_transfer_hardware = pic32_spi_unprepare_hardware;
+ master->use_gpio_descriptors = true;
/* optional DMA support */
ret = pic32_spi_dma_prep(pic32s, &pdev->dev);
diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c
index 2e134eb4bd2c..861b21c63504 100644
--- a/drivers/spi/spi-pxa2xx-pci.c
+++ b/drivers/spi/spi-pxa2xx-pci.c
@@ -15,32 +15,20 @@
#include <linux/dmaengine.h>
#include <linux/platform_data/dma-dw.h>
-enum {
- PORT_QUARK_X1000,
- PORT_BYT,
- PORT_MRFLD,
- PORT_BSW0,
- PORT_BSW1,
- PORT_BSW2,
- PORT_CE4100,
- PORT_LPT0,
- PORT_LPT1,
-};
+#define PCI_DEVICE_ID_INTEL_QUARK_X1000 0x0935
+#define PCI_DEVICE_ID_INTEL_BYT 0x0f0e
+#define PCI_DEVICE_ID_INTEL_MRFLD 0x1194
+#define PCI_DEVICE_ID_INTEL_BSW0 0x228e
+#define PCI_DEVICE_ID_INTEL_BSW1 0x2290
+#define PCI_DEVICE_ID_INTEL_BSW2 0x22ac
+#define PCI_DEVICE_ID_INTEL_CE4100 0x2e6a
+#define PCI_DEVICE_ID_INTEL_LPT0_0 0x9c65
+#define PCI_DEVICE_ID_INTEL_LPT0_1 0x9c66
+#define PCI_DEVICE_ID_INTEL_LPT1_0 0x9ce5
+#define PCI_DEVICE_ID_INTEL_LPT1_1 0x9ce6
struct pxa_spi_info {
- enum pxa_ssp_type type;
- int port_id;
- int num_chipselect;
- unsigned long max_clk_rate;
-
- /* DMA channel request parameters */
- bool (*dma_filter)(struct dma_chan *chan, void *param);
- void *tx_param;
- void *rx_param;
-
- int dma_burst_size;
-
- int (*setup)(struct pci_dev *pdev, struct pxa_spi_info *c);
+ int (*setup)(struct pci_dev *pdev, struct pxa2xx_spi_controller *c);
};
static struct dw_dma_slave byt_tx_param = { .dst_id = 0 };
@@ -65,6 +53,24 @@ static struct dw_dma_slave lpt1_rx_param = { .src_id = 1 };
static struct dw_dma_slave lpt0_tx_param = { .dst_id = 2 };
static struct dw_dma_slave lpt0_rx_param = { .src_id = 3 };
+static void pxa2xx_spi_pci_clk_unregister(void *clk)
+{
+ clk_unregister(clk);
+}
+
+static int pxa2xx_spi_pci_clk_register(struct pci_dev *dev, struct ssp_device *ssp,
+ unsigned long rate)
+{
+ char buf[40];
+
+ snprintf(buf, sizeof(buf), "pxa2xx-spi.%d", ssp->port_id);
+ ssp->clk = clk_register_fixed_rate(&dev->dev, buf, NULL, 0, rate);
+ if (IS_ERR(ssp->clk))
+ return PTR_ERR(ssp->clk);
+
+ return devm_add_action_or_reset(&dev->dev, pxa2xx_spi_pci_clk_unregister, ssp->clk);
+}
+
static bool lpss_dma_filter(struct dma_chan *chan, void *param)
{
struct dw_dma_slave *dws = param;
@@ -76,55 +82,131 @@ static bool lpss_dma_filter(struct dma_chan *chan, void *param)
return true;
}
-static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c)
+static void lpss_dma_put_device(void *dma_dev)
{
+ pci_dev_put(dma_dev);
+}
+
+static int lpss_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c)
+{
+ struct ssp_device *ssp = &c->ssp;
+ struct dw_dma_slave *tx, *rx;
struct pci_dev *dma_dev;
+ int ret;
- c->num_chipselect = 1;
- c->max_clk_rate = 50000000;
+ switch (dev->device) {
+ case PCI_DEVICE_ID_INTEL_BYT:
+ ssp->type = LPSS_BYT_SSP;
+ ssp->port_id = 0;
+ c->tx_param = &byt_tx_param;
+ c->rx_param = &byt_rx_param;
+ break;
+ case PCI_DEVICE_ID_INTEL_BSW0:
+ ssp->type = LPSS_BSW_SSP;
+ ssp->port_id = 0;
+ c->tx_param = &bsw0_tx_param;
+ c->rx_param = &bsw0_rx_param;
+ break;
+ case PCI_DEVICE_ID_INTEL_BSW1:
+ ssp->type = LPSS_BSW_SSP;
+ ssp->port_id = 1;
+ c->tx_param = &bsw1_tx_param;
+ c->rx_param = &bsw1_rx_param;
+ break;
+ case PCI_DEVICE_ID_INTEL_BSW2:
+ ssp->type = LPSS_BSW_SSP;
+ ssp->port_id = 2;
+ c->tx_param = &bsw2_tx_param;
+ c->rx_param = &bsw2_rx_param;
+ break;
+ case PCI_DEVICE_ID_INTEL_LPT0_0:
+ case PCI_DEVICE_ID_INTEL_LPT1_0:
+ ssp->type = LPSS_LPT_SSP;
+ ssp->port_id = 0;
+ c->tx_param = &lpt0_tx_param;
+ c->rx_param = &lpt0_rx_param;
+ break;
+ case PCI_DEVICE_ID_INTEL_LPT0_1:
+ case PCI_DEVICE_ID_INTEL_LPT1_1:
+ ssp->type = LPSS_LPT_SSP;
+ ssp->port_id = 1;
+ c->tx_param = &lpt1_tx_param;
+ c->rx_param = &lpt1_rx_param;
+ break;
+ default:
+ return -ENODEV;
+ }
- dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+ c->num_chipselect = 1;
- if (c->tx_param) {
- struct dw_dma_slave *slave = c->tx_param;
+ ret = pxa2xx_spi_pci_clk_register(dev, ssp, 50000000);
+ if (ret)
+ return ret;
- slave->dma_dev = &dma_dev->dev;
- slave->m_master = 0;
- slave->p_master = 1;
- }
+ dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+ ret = devm_add_action_or_reset(&dev->dev, lpss_dma_put_device, dma_dev);
+ if (ret)
+ return ret;
- if (c->rx_param) {
- struct dw_dma_slave *slave = c->rx_param;
+ tx = c->tx_param;
+ tx->dma_dev = &dma_dev->dev;
+ tx->m_master = 0;
+ tx->p_master = 1;
- slave->dma_dev = &dma_dev->dev;
- slave->m_master = 0;
- slave->p_master = 1;
- }
+ rx = c->rx_param;
+ rx->dma_dev = &dma_dev->dev;
+ rx->m_master = 0;
+ rx->p_master = 1;
c->dma_filter = lpss_dma_filter;
+ c->dma_burst_size = 1;
+ c->enable_dma = 1;
return 0;
}
-static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c)
+static const struct pxa_spi_info lpss_info_config = {
+ .setup = lpss_spi_setup,
+};
+
+static int ce4100_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c)
{
- struct pci_dev *dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(21, 0));
+ struct ssp_device *ssp = &c->ssp;
+
+ ssp->type = PXA25x_SSP;
+ ssp->port_id = dev->devfn;
+ c->num_chipselect = dev->devfn;
+
+ return pxa2xx_spi_pci_clk_register(dev, ssp, 3686400);
+}
+
+static const struct pxa_spi_info ce4100_info_config = {
+ .setup = ce4100_spi_setup,
+};
+
+static int mrfld_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c)
+{
+ struct ssp_device *ssp = &c->ssp;
struct dw_dma_slave *tx, *rx;
+ struct pci_dev *dma_dev;
+ int ret;
+
+ ssp->type = MRFLD_SSP;
switch (PCI_FUNC(dev->devfn)) {
case 0:
- c->port_id = 3;
+ ssp->port_id = 3;
c->num_chipselect = 1;
c->tx_param = &mrfld3_tx_param;
c->rx_param = &mrfld3_rx_param;
break;
case 1:
- c->port_id = 5;
+ ssp->port_id = 5;
c->num_chipselect = 4;
c->tx_param = &mrfld5_tx_param;
c->rx_param = &mrfld5_rx_param;
break;
case 2:
- c->port_id = 6;
+ ssp->port_id = 6;
c->num_chipselect = 1;
c->tx_param = &mrfld6_tx_param;
c->rx_param = &mrfld6_rx_param;
@@ -133,6 +215,15 @@ static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c)
return -ENODEV;
}
+ ret = pxa2xx_spi_pci_clk_register(dev, ssp, 25000000);
+ if (ret)
+ return ret;
+
+ dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(21, 0));
+ ret = devm_add_action_or_reset(&dev->dev, lpss_dma_put_device, dma_dev);
+ if (ret)
+ return ret;
+
tx = c->tx_param;
tx->dma_dev = &dma_dev->dev;
@@ -141,81 +232,38 @@ static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c)
c->dma_filter = lpss_dma_filter;
c->dma_burst_size = 8;
+ c->enable_dma = 1;
return 0;
}
-static struct pxa_spi_info spi_info_configs[] = {
- [PORT_CE4100] = {
- .type = PXA25x_SSP,
- .port_id = -1,
- .num_chipselect = -1,
- .max_clk_rate = 3686400,
- },
- [PORT_BYT] = {
- .type = LPSS_BYT_SSP,
- .port_id = 0,
- .setup = lpss_spi_setup,
- .tx_param = &byt_tx_param,
- .rx_param = &byt_rx_param,
- },
- [PORT_BSW0] = {
- .type = LPSS_BSW_SSP,
- .port_id = 0,
- .setup = lpss_spi_setup,
- .tx_param = &bsw0_tx_param,
- .rx_param = &bsw0_rx_param,
- },
- [PORT_BSW1] = {
- .type = LPSS_BSW_SSP,
- .port_id = 1,
- .setup = lpss_spi_setup,
- .tx_param = &bsw1_tx_param,
- .rx_param = &bsw1_rx_param,
- },
- [PORT_BSW2] = {
- .type = LPSS_BSW_SSP,
- .port_id = 2,
- .setup = lpss_spi_setup,
- .tx_param = &bsw2_tx_param,
- .rx_param = &bsw2_rx_param,
- },
- [PORT_MRFLD] = {
- .type = MRFLD_SSP,
- .max_clk_rate = 25000000,
- .setup = mrfld_spi_setup,
- },
- [PORT_QUARK_X1000] = {
- .type = QUARK_X1000_SSP,
- .port_id = -1,
- .num_chipselect = 1,
- .max_clk_rate = 50000000,
- },
- [PORT_LPT0] = {
- .type = LPSS_LPT_SSP,
- .port_id = 0,
- .setup = lpss_spi_setup,
- .tx_param = &lpt0_tx_param,
- .rx_param = &lpt0_rx_param,
- },
- [PORT_LPT1] = {
- .type = LPSS_LPT_SSP,
- .port_id = 1,
- .setup = lpss_spi_setup,
- .tx_param = &lpt1_tx_param,
- .rx_param = &lpt1_rx_param,
- },
+static const struct pxa_spi_info mrfld_info_config = {
+ .setup = mrfld_spi_setup,
+};
+
+static int qrk_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c)
+{
+ struct ssp_device *ssp = &c->ssp;
+
+ ssp->type = QUARK_X1000_SSP;
+ ssp->port_id = dev->devfn;
+ c->num_chipselect = 1;
+
+ return pxa2xx_spi_pci_clk_register(dev, ssp, 50000000);
+}
+
+static const struct pxa_spi_info qrk_info_config = {
+ .setup = qrk_spi_setup,
};
static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
const struct pci_device_id *ent)
{
+ const struct pxa_spi_info *info;
struct platform_device_info pi;
int ret;
struct platform_device *pdev;
struct pxa2xx_spi_controller spi_pdata;
struct ssp_device *ssp;
- struct pxa_spi_info *c;
- char buf[40];
ret = pcim_enable_device(dev);
if (ret)
@@ -225,27 +273,17 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
if (ret)
return ret;
- c = &spi_info_configs[ent->driver_data];
- if (c->setup) {
- ret = c->setup(dev, c);
- if (ret)
- return ret;
- }
-
memset(&spi_pdata, 0, sizeof(spi_pdata));
- spi_pdata.num_chipselect = (c->num_chipselect > 0) ? c->num_chipselect : dev->devfn;
- spi_pdata.dma_filter = c->dma_filter;
- spi_pdata.tx_param = c->tx_param;
- spi_pdata.rx_param = c->rx_param;
- spi_pdata.enable_dma = c->rx_param && c->tx_param;
- spi_pdata.dma_burst_size = c->dma_burst_size ? c->dma_burst_size : 1;
ssp = &spi_pdata.ssp;
ssp->dev = &dev->dev;
ssp->phys_base = pci_resource_start(dev, 0);
ssp->mmio_base = pcim_iomap_table(dev)[0];
- ssp->port_id = (c->port_id >= 0) ? c->port_id : dev->devfn;
- ssp->type = c->type;
+
+ info = (struct pxa_spi_info *)ent->driver_data;
+ ret = info->setup(dev, &spi_pdata);
+ if (ret)
+ return ret;
pci_set_master(dev);
@@ -254,14 +292,8 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
return ret;
ssp->irq = pci_irq_vector(dev, 0);
- snprintf(buf, sizeof(buf), "pxa2xx-spi.%d", ssp->port_id);
- ssp->clk = clk_register_fixed_rate(&dev->dev, buf, NULL, 0,
- c->max_clk_rate);
- if (IS_ERR(ssp->clk))
- return PTR_ERR(ssp->clk);
-
memset(&pi, 0, sizeof(pi));
- pi.fwnode = dev->dev.fwnode;
+ pi.fwnode = dev_fwnode(&dev->dev);
pi.parent = &dev->dev;
pi.name = "pxa2xx-spi";
pi.id = ssp->port_id;
@@ -269,10 +301,8 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
pi.size_data = sizeof(spi_pdata);
pdev = platform_device_register_full(&pi);
- if (IS_ERR(pdev)) {
- clk_unregister(ssp->clk);
+ if (IS_ERR(pdev))
return PTR_ERR(pdev);
- }
pci_set_drvdata(dev, pdev);
@@ -282,26 +312,22 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
static void pxa2xx_spi_pci_remove(struct pci_dev *dev)
{
struct platform_device *pdev = pci_get_drvdata(dev);
- struct pxa2xx_spi_controller *spi_pdata;
-
- spi_pdata = dev_get_platdata(&pdev->dev);
platform_device_unregister(pdev);
- clk_unregister(spi_pdata->ssp.clk);
}
static const struct pci_device_id pxa2xx_spi_pci_devices[] = {
- { PCI_VDEVICE(INTEL, 0x0935), PORT_QUARK_X1000 },
- { PCI_VDEVICE(INTEL, 0x0f0e), PORT_BYT },
- { PCI_VDEVICE(INTEL, 0x1194), PORT_MRFLD },
- { PCI_VDEVICE(INTEL, 0x228e), PORT_BSW0 },
- { PCI_VDEVICE(INTEL, 0x2290), PORT_BSW1 },
- { PCI_VDEVICE(INTEL, 0x22ac), PORT_BSW2 },
- { PCI_VDEVICE(INTEL, 0x2e6a), PORT_CE4100 },
- { PCI_VDEVICE(INTEL, 0x9c65), PORT_LPT0 },
- { PCI_VDEVICE(INTEL, 0x9c66), PORT_LPT1 },
- { PCI_VDEVICE(INTEL, 0x9ce5), PORT_LPT0 },
- { PCI_VDEVICE(INTEL, 0x9ce6), PORT_LPT1 },
+ { PCI_DEVICE_DATA(INTEL, QUARK_X1000, &qrk_info_config) },
+ { PCI_DEVICE_DATA(INTEL, BYT, &lpss_info_config) },
+ { PCI_DEVICE_DATA(INTEL, MRFLD, &mrfld_info_config) },
+ { PCI_DEVICE_DATA(INTEL, BSW0, &lpss_info_config) },
+ { PCI_DEVICE_DATA(INTEL, BSW1, &lpss_info_config) },
+ { PCI_DEVICE_DATA(INTEL, BSW2, &lpss_info_config) },
+ { PCI_DEVICE_DATA(INTEL, CE4100, &ce4100_info_config) },
+ { PCI_DEVICE_DATA(INTEL, LPT0_0, &lpss_info_config) },
+ { PCI_DEVICE_DATA(INTEL, LPT0_1, &lpss_info_config) },
+ { PCI_DEVICE_DATA(INTEL, LPT1_0, &lpss_info_config) },
+ { PCI_DEVICE_DATA(INTEL, LPT1_1, &lpss_info_config) },
{ }
};
MODULE_DEVICE_TABLE(pci, pxa2xx_spi_pci_devices);
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index e88f86274eeb..edb42d08857d 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -13,7 +13,6 @@
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
-#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
@@ -1163,57 +1162,6 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_controller *controller)
return 0;
}
-static void cleanup_cs(struct spi_device *spi)
-{
- if (!gpio_is_valid(spi->cs_gpio))
- return;
-
- gpio_free(spi->cs_gpio);
- spi->cs_gpio = -ENOENT;
-}
-
-static int setup_cs(struct spi_device *spi, struct chip_data *chip,
- struct pxa2xx_spi_chip *chip_info)
-{
- struct driver_data *drv_data = spi_controller_get_devdata(spi->controller);
-
- if (chip == NULL)
- return 0;
-
- if (chip_info == NULL)
- return 0;
-
- if (drv_data->ssp_type == CE4100_SSP)
- return 0;
-
- /*
- * NOTE: setup() can be called multiple times, possibly with
- * different chip_info, release previously requested GPIO.
- */
- cleanup_cs(spi);
-
- if (gpio_is_valid(chip_info->gpio_cs)) {
- int gpio = chip_info->gpio_cs;
- int err;
-
- err = gpio_request(gpio, "SPI_CS");
- if (err) {
- dev_err(&spi->dev, "failed to request chip select GPIO%d\n", gpio);
- return err;
- }
-
- err = gpio_direction_output(gpio, !(spi->mode & SPI_CS_HIGH));
- if (err) {
- gpio_free(gpio);
- return err;
- }
-
- spi->cs_gpio = gpio;
- }
-
- return 0;
-}
-
static int setup(struct spi_device *spi)
{
struct pxa2xx_spi_chip *chip_info;
@@ -1222,7 +1170,6 @@ static int setup(struct spi_device *spi)
struct driver_data *drv_data =
spi_controller_get_devdata(spi->controller);
uint tx_thres, tx_hi_thres, rx_thres;
- int err;
switch (drv_data->ssp_type) {
case QUARK_X1000_SSP:
@@ -1365,21 +1312,13 @@ static int setup(struct spi_device *spi)
spi_set_ctldata(spi, chip);
- if (drv_data->ssp_type == CE4100_SSP)
- return 0;
-
- err = setup_cs(spi, chip, chip_info);
- if (err)
- kfree(chip);
-
- return err;
+ return 0;
}
static void cleanup(struct spi_device *spi)
{
struct chip_data *chip = spi_get_ctldata(spi);
- cleanup_cs(spi);
kfree(chip);
}
@@ -1455,6 +1394,11 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = {
{ PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP },
{ PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP },
{ PCI_VDEVICE(INTEL, 0x5ac6), LPSS_BXT_SSP },
+ /* RPL-S */
+ { PCI_VDEVICE(INTEL, 0x7a2a), LPSS_CNL_SSP },
+ { PCI_VDEVICE(INTEL, 0x7a2b), LPSS_CNL_SSP },
+ { PCI_VDEVICE(INTEL, 0x7a79), LPSS_CNL_SSP },
+ { PCI_VDEVICE(INTEL, 0x7a7b), LPSS_CNL_SSP },
/* ADL-S */
{ PCI_VDEVICE(INTEL, 0x7aaa), LPSS_CNL_SSP },
{ PCI_VDEVICE(INTEL, 0x7aab), LPSS_CNL_SSP },
diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index d39dec6d1c91..00d6084306b4 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -593,7 +593,6 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
{
struct spi_qup *controller = dev_id;
u32 opflags, qup_err, spi_err;
- unsigned long flags;
int error = 0;
qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
@@ -625,10 +624,10 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
error = -EIO;
}
- spin_lock_irqsave(&controller->lock, flags);
+ spin_lock(&controller->lock);
if (!controller->error)
controller->error = error;
- spin_unlock_irqrestore(&controller->lock, flags);
+ spin_unlock(&controller->lock);
if (spi_qup_is_dma_xfer(controller->mode)) {
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c
index a46b38544027..bd87d3c92dd3 100644
--- a/drivers/spi/spi-rockchip-sfc.c
+++ b/drivers/spi/spi-rockchip-sfc.c
@@ -624,10 +624,8 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
/* Find the irq */
ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- dev_err(dev, "Failed to get the irq\n");
+ if (ret < 0)
goto err_irq;
- }
ret = devm_request_irq(dev, ret, rockchip_sfc_irq_handler,
0, pdev->name, sfc);
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index c6a1bb09be05..cdc16eecaf6b 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -133,7 +133,8 @@
#define INT_TF_OVERFLOW (1 << 1)
#define INT_RF_UNDERFLOW (1 << 2)
#define INT_RF_OVERFLOW (1 << 3)
-#define INT_RF_FULL (1 << 4)
+#define INT_RF_FULL (1 << 4)
+#define INT_CS_INACTIVE (1 << 6)
/* Bit fields in ICR, 4bit */
#define ICR_MASK 0x0f
@@ -194,6 +195,8 @@ struct rockchip_spi {
bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM];
bool slave_abort;
+ bool cs_inactive; /* spi slave tansmition stop when cs inactive */
+ struct spi_transfer *xfer; /* Store xfer temporarily */
};
static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable)
@@ -275,8 +278,9 @@ static void rockchip_spi_handle_err(struct spi_controller *ctlr,
*/
spi_enable_chip(rs, false);
- /* make sure all interrupts are masked */
+ /* make sure all interrupts are masked and status cleared */
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
+ writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR);
if (atomic_read(&rs->state) & TXDMA)
dmaengine_terminate_async(ctlr->dma_tx);
@@ -343,6 +347,15 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id)
struct spi_controller *ctlr = dev_id;
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
+ /* When int_cs_inactive comes, spi slave abort */
+ if (rs->cs_inactive && readl_relaxed(rs->regs + ROCKCHIP_SPI_IMR) & INT_CS_INACTIVE) {
+ ctlr->slave_abort(ctlr);
+ writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
+ writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR);
+
+ return IRQ_HANDLED;
+ }
+
if (rs->tx_left)
rockchip_spi_pio_writer(rs);
@@ -350,6 +363,7 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id)
if (!rs->rx_left) {
spi_enable_chip(rs, false);
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
+ writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR);
spi_finalize_current_transfer(ctlr);
}
@@ -357,14 +371,18 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id)
}
static int rockchip_spi_prepare_irq(struct rockchip_spi *rs,
- struct spi_transfer *xfer)
+ struct spi_controller *ctlr,
+ struct spi_transfer *xfer)
{
rs->tx = xfer->tx_buf;
rs->rx = xfer->rx_buf;
rs->tx_left = rs->tx ? xfer->len / rs->n_bytes : 0;
rs->rx_left = xfer->len / rs->n_bytes;
- writel_relaxed(INT_RF_FULL, rs->regs + ROCKCHIP_SPI_IMR);
+ if (rs->cs_inactive)
+ writel_relaxed(INT_RF_FULL | INT_CS_INACTIVE, rs->regs + ROCKCHIP_SPI_IMR);
+ else
+ writel_relaxed(INT_RF_FULL, rs->regs + ROCKCHIP_SPI_IMR);
spi_enable_chip(rs, true);
if (rs->tx_left)
@@ -383,6 +401,9 @@ static void rockchip_spi_dma_rxcb(void *data)
if (state & TXDMA && !rs->slave_abort)
return;
+ if (rs->cs_inactive)
+ writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
+
spi_enable_chip(rs, false);
spi_finalize_current_transfer(ctlr);
}
@@ -423,14 +444,16 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
atomic_set(&rs->state, 0);
+ rs->tx = xfer->tx_buf;
+ rs->rx = xfer->rx_buf;
+
rxdesc = NULL;
if (xfer->rx_buf) {
struct dma_slave_config rxconf = {
.direction = DMA_DEV_TO_MEM,
.src_addr = rs->dma_addr_rx,
.src_addr_width = rs->n_bytes,
- .src_maxburst = rockchip_spi_calc_burst_size(xfer->len /
- rs->n_bytes),
+ .src_maxburst = rockchip_spi_calc_burst_size(xfer->len / rs->n_bytes),
};
dmaengine_slave_config(ctlr->dma_rx, &rxconf);
@@ -474,10 +497,13 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
/* rx must be started before tx due to spi instinct */
if (rxdesc) {
atomic_or(RXDMA, &rs->state);
- dmaengine_submit(rxdesc);
+ ctlr->dma_rx->cookie = dmaengine_submit(rxdesc);
dma_async_issue_pending(ctlr->dma_rx);
}
+ if (rs->cs_inactive)
+ writel_relaxed(INT_CS_INACTIVE, rs->regs + ROCKCHIP_SPI_IMR);
+
spi_enable_chip(rs, true);
if (txdesc) {
@@ -584,7 +610,42 @@ static size_t rockchip_spi_max_transfer_size(struct spi_device *spi)
static int rockchip_spi_slave_abort(struct spi_controller *ctlr)
{
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
+ u32 rx_fifo_left;
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ /* Get current dma rx point */
+ if (atomic_read(&rs->state) & RXDMA) {
+ dmaengine_pause(ctlr->dma_rx);
+ status = dmaengine_tx_status(ctlr->dma_rx, ctlr->dma_rx->cookie, &state);
+ if (status == DMA_ERROR) {
+ rs->rx = rs->xfer->rx_buf;
+ rs->xfer->len = 0;
+ rx_fifo_left = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
+ for (; rx_fifo_left; rx_fifo_left--)
+ readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR);
+ goto out;
+ } else {
+ rs->rx += rs->xfer->len - rs->n_bytes * state.residue;
+ }
+ }
+
+ /* Get the valid data left in rx fifo and set rs->xfer->len real rx size */
+ if (rs->rx) {
+ rx_fifo_left = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
+ for (; rx_fifo_left; rx_fifo_left--) {
+ u32 rxw = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR);
+
+ if (rs->n_bytes == 1)
+ *(u8 *)rs->rx = (u8)rxw;
+ else
+ *(u16 *)rs->rx = (u16)rxw;
+ rs->rx += rs->n_bytes;
+ }
+ rs->xfer->len = (unsigned int)(rs->rx - rs->xfer->rx_buf);
+ }
+out:
if (atomic_read(&rs->state) & RXDMA)
dmaengine_terminate_sync(ctlr->dma_rx);
if (atomic_read(&rs->state) & TXDMA)
@@ -626,7 +687,7 @@ static int rockchip_spi_transfer_one(
}
rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2;
-
+ rs->xfer = xfer;
use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false;
ret = rockchip_spi_config(rs, spi, xfer, use_dma, ctlr->slave);
@@ -636,7 +697,7 @@ static int rockchip_spi_transfer_one(
if (use_dma)
return rockchip_spi_prepare_dma(rs, ctlr, xfer);
- return rockchip_spi_prepare_irq(rs, xfer);
+ return rockchip_spi_prepare_irq(rs, ctlr, xfer);
}
static bool rockchip_spi_can_dma(struct spi_controller *ctlr,
@@ -653,6 +714,29 @@ static bool rockchip_spi_can_dma(struct spi_controller *ctlr,
return xfer->len / bytes_per_word >= rs->fifo_len;
}
+static int rockchip_spi_setup(struct spi_device *spi)
+{
+ struct rockchip_spi *rs = spi_controller_get_devdata(spi->controller);
+ u32 cr0;
+
+ pm_runtime_get_sync(rs->dev);
+
+ cr0 = readl_relaxed(rs->regs + ROCKCHIP_SPI_CTRLR0);
+
+ cr0 &= ~(0x3 << CR0_SCPH_OFFSET);
+ cr0 |= ((spi->mode & 0x3) << CR0_SCPH_OFFSET);
+ if (spi->mode & SPI_CS_HIGH && spi->chip_select <= 1)
+ cr0 |= BIT(spi->chip_select) << CR0_SOI_OFFSET;
+ else if (spi->chip_select <= 1)
+ cr0 &= ~(BIT(spi->chip_select) << CR0_SOI_OFFSET);
+
+ writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0);
+
+ pm_runtime_put(rs->dev);
+
+ return 0;
+}
+
static int rockchip_spi_probe(struct platform_device *pdev)
{
int ret;
@@ -780,6 +864,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
ctlr->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX;
ctlr->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT);
+ ctlr->setup = rockchip_spi_setup;
ctlr->set_cs = rockchip_spi_set_cs;
ctlr->transfer_one = rockchip_spi_transfer_one;
ctlr->max_transfer_size = rockchip_spi_max_transfer_size;
@@ -815,8 +900,13 @@ static int rockchip_spi_probe(struct platform_device *pdev)
switch (readl_relaxed(rs->regs + ROCKCHIP_SPI_VERSION)) {
case ROCKCHIP_SPI_VER2_TYPE2:
ctlr->mode_bits |= SPI_CS_HIGH;
+ if (ctlr->can_dma && slave_mode)
+ rs->cs_inactive = true;
+ else
+ rs->cs_inactive = false;
break;
default:
+ rs->cs_inactive = false;
break;
}
@@ -875,14 +965,14 @@ static int rockchip_spi_suspend(struct device *dev)
{
int ret;
struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
ret = spi_controller_suspend(ctlr);
if (ret < 0)
return ret;
- ret = pm_runtime_force_suspend(dev);
- if (ret < 0)
- return ret;
+ clk_disable_unprepare(rs->spiclk);
+ clk_disable_unprepare(rs->apb_pclk);
pinctrl_pm_select_sleep_state(dev);
@@ -897,10 +987,14 @@ static int rockchip_spi_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
- ret = pm_runtime_force_resume(dev);
+ ret = clk_prepare_enable(rs->apb_pclk);
if (ret < 0)
return ret;
+ ret = clk_prepare_enable(rs->spiclk);
+ if (ret < 0)
+ clk_disable_unprepare(rs->apb_pclk);
+
ret = spi_controller_resume(ctlr);
if (ret < 0) {
clk_disable_unprepare(rs->spiclk);
@@ -942,7 +1036,7 @@ static int rockchip_spi_runtime_resume(struct device *dev)
#endif /* CONFIG_PM */
static const struct dev_pm_ops rockchip_spi_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
SET_RUNTIME_PM_OPS(rockchip_spi_runtime_suspend,
rockchip_spi_runtime_resume, NULL)
};
diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c
index d6f51695ca5b..660aa866af06 100644
--- a/drivers/spi/spi-s3c24xx.c
+++ b/drivers/spi/spi-s3c24xx.c
@@ -12,7 +12,6 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -62,9 +61,6 @@ struct s3c24xx_spi {
unsigned char fiq_inuse;
unsigned char fiq_claimed;
- void (*set_cs)(struct s3c2410_spi_info *spi,
- int cs, int pol);
-
/* data buffers */
const unsigned char *tx;
unsigned char *rx;
@@ -84,29 +80,21 @@ static inline struct s3c24xx_spi *to_hw(struct spi_device *sdev)
return spi_master_get_devdata(sdev->master);
}
-static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol)
-{
- gpio_set_value(spi->pin_cs, pol);
-}
-
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);
- unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
/* change the chipselect state and the state of the spi engine clock */
switch (value) {
case BITBANG_CS_INACTIVE:
- hw->set_cs(hw->pdata, spi->chip_select, cspol^1);
writeb(cs->spcon, hw->regs + S3C2410_SPCON);
break;
case BITBANG_CS_ACTIVE:
writeb(cs->spcon | S3C2410_SPCON_ENSCK,
hw->regs + S3C2410_SPCON);
- hw->set_cs(hw->pdata, spi->chip_select, cspol);
break;
}
}
@@ -452,14 +440,6 @@ static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw)
writeb(0xff, hw->regs + S3C2410_SPPRE);
writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);
writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);
-
- if (hw->pdata) {
- if (hw->set_cs == s3c24xx_spi_gpiocs)
- gpio_direction_output(hw->pdata->pin_cs, 1);
-
- if (hw->pdata->gpio_setup)
- hw->pdata->gpio_setup(hw->pdata, 1);
- }
}
static int s3c24xx_spi_probe(struct platform_device *pdev)
@@ -502,6 +482,9 @@ static int s3c24xx_spi_probe(struct platform_device *pdev)
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 */
@@ -541,27 +524,6 @@ static int s3c24xx_spi_probe(struct platform_device *pdev)
goto err_no_pdata;
}
- /* setup any gpio we can */
-
- if (!pdata->set_cs) {
- if (pdata->pin_cs < 0) {
- dev_err(&pdev->dev, "No chipselect pin\n");
- err = -EINVAL;
- goto err_register;
- }
-
- err = devm_gpio_request(&pdev->dev, pdata->pin_cs,
- dev_name(&pdev->dev));
- if (err) {
- dev_err(&pdev->dev, "Failed to get gpio for cs\n");
- goto err_register;
- }
-
- hw->set_cs = s3c24xx_spi_gpiocs;
- gpio_direction_output(pdata->pin_cs, 1);
- } else
- hw->set_cs = pdata->set_cs;
-
s3c24xx_spi_initialsetup(hw);
/* register our spi controller */
@@ -604,9 +566,6 @@ static int s3c24xx_spi_suspend(struct device *dev)
if (ret)
return ret;
- if (hw->pdata && hw->pdata->gpio_setup)
- hw->pdata->gpio_setup(hw->pdata, 0);
-
clk_disable(hw->clk);
return 0;
}
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 8755cd85e83c..c26440e9058d 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -13,10 +13,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
-#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <linux/platform_data/spi-s3c64xx.h>
@@ -656,7 +654,11 @@ static int s3c64xx_spi_prepare_message(struct spi_master *master,
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
/* Configure feedback delay */
- writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
+ if (!cs)
+ /* No delay if not defined */
+ writel(0, sdd->regs + S3C64XX_SPI_FB_CLK);
+ else
+ writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
return 0;
}
@@ -796,16 +798,14 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
return ERR_PTR(-EINVAL);
}
- data_np = of_get_child_by_name(slave_np, "controller-data");
- if (!data_np) {
- dev_err(&spi->dev, "child node 'controller-data' not found\n");
- return ERR_PTR(-EINVAL);
- }
-
cs = kzalloc(sizeof(*cs), GFP_KERNEL);
- if (!cs) {
- of_node_put(data_np);
+ if (!cs)
return ERR_PTR(-ENOMEM);
+
+ data_np = of_get_child_by_name(slave_np, "controller-data");
+ if (!data_np) {
+ dev_info(&spi->dev, "feedback delay set to default (0)\n");
+ return cs;
}
of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay);
@@ -830,34 +830,16 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
if (spi->dev.of_node) {
cs = s3c64xx_get_slave_ctrldata(spi);
spi->controller_data = cs;
- } else if (cs) {
- /* On non-DT platforms the SPI core will set spi->cs_gpio
- * to -ENOENT. The GPIO pin used to drive the chip select
- * is defined by using platform data so spi->cs_gpio value
- * has to be override to have the proper GPIO pin number.
- */
- spi->cs_gpio = cs->line;
}
- if (IS_ERR_OR_NULL(cs)) {
+ /* NULL is fine, we just avoid using the FB delay (=0) */
+ if (IS_ERR(cs)) {
dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select);
return -ENODEV;
}
- if (!spi_get_ctldata(spi)) {
- if (gpio_is_valid(spi->cs_gpio)) {
- err = gpio_request_one(spi->cs_gpio, GPIOF_OUT_INIT_HIGH,
- dev_name(&spi->dev));
- if (err) {
- dev_err(&spi->dev,
- "Failed to get /CS gpio [%d]: %d\n",
- spi->cs_gpio, err);
- goto err_gpio_req;
- }
- }
-
+ if (!spi_get_ctldata(spi))
spi_set_ctldata(spi, cs);
- }
pm_runtime_get_sync(&sdd->pdev->dev);
@@ -909,11 +891,9 @@ setup_exit:
/* setup() returns with device de-selected */
s3c64xx_spi_set_cs(spi, false);
- if (gpio_is_valid(spi->cs_gpio))
- gpio_free(spi->cs_gpio);
spi_set_ctldata(spi, NULL);
-err_gpio_req:
+ /* This was dynamically allocated on the DT path */
if (spi->dev.of_node)
kfree(cs);
@@ -924,19 +904,9 @@ static void s3c64xx_spi_cleanup(struct spi_device *spi)
{
struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi);
- if (gpio_is_valid(spi->cs_gpio)) {
- gpio_free(spi->cs_gpio);
- if (spi->dev.of_node)
- kfree(cs);
- else {
- /* On non-DT platforms, the SPI core sets
- * spi->cs_gpio to -ENOENT and .setup()
- * overrides it with the GPIO pin value
- * passed using platform data.
- */
- spi->cs_gpio = -ENOENT;
- }
- }
+ /* This was dynamically allocated on the DT path */
+ if (spi->dev.of_node)
+ kfree(cs);
spi_set_ctldata(spi, NULL);
}
@@ -1131,6 +1101,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
master->prepare_message = s3c64xx_spi_prepare_message;
master->transfer_one = s3c64xx_spi_transfer_one;
master->num_chipselect = sci->num_cs;
+ master->use_gpio_descriptors = true;
master->dma_alignment = 8;
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
SPI_BPW_MASK(8);
@@ -1442,6 +1413,16 @@ static const struct s3c64xx_spi_port_config exynos5433_spi_port_config = {
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
};
+static struct s3c64xx_spi_port_config fsd_spi_port_config = {
+ .fifo_lvl_mask = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f},
+ .rx_lvl_offset = 15,
+ .tx_st_done = 25,
+ .high_speed = true,
+ .clk_from_cmu = true,
+ .clk_ioclk = false,
+ .quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
+};
+
static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
{
.name = "s3c2443-spi",
@@ -1472,6 +1453,9 @@ static const struct of_device_id s3c64xx_spi_dt_match[] = {
{ .compatible = "samsung,exynos5433-spi",
.data = (void *)&exynos5433_spi_port_config,
},
+ { .compatible = "tesla,fsd-spi",
+ .data = (void *)&fsd_spi_port_config,
+ },
{ },
};
MODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match);
diff --git a/drivers/spi/spi-slave-system-control.c b/drivers/spi/spi-slave-system-control.c
index 169f3d595f60..d37cfe995a63 100644
--- a/drivers/spi/spi-slave-system-control.c
+++ b/drivers/spi/spi-slave-system-control.c
@@ -132,13 +132,12 @@ static int spi_slave_system_control_probe(struct spi_device *spi)
return 0;
}
-static int spi_slave_system_control_remove(struct spi_device *spi)
+static void spi_slave_system_control_remove(struct spi_device *spi)
{
struct spi_slave_system_control_priv *priv = spi_get_drvdata(spi);
spi_slave_abort(spi);
wait_for_completion(&priv->finished);
- return 0;
}
static struct spi_driver spi_slave_system_control_driver = {
diff --git a/drivers/spi/spi-slave-time.c b/drivers/spi/spi-slave-time.c
index f2e07a392d68..f56c1afb8534 100644
--- a/drivers/spi/spi-slave-time.c
+++ b/drivers/spi/spi-slave-time.c
@@ -106,13 +106,12 @@ static int spi_slave_time_probe(struct spi_device *spi)
return 0;
}
-static int spi_slave_time_remove(struct spi_device *spi)
+static void spi_slave_time_remove(struct spi_device *spi)
{
struct spi_slave_time_priv *priv = spi_get_drvdata(spi);
spi_slave_abort(spi);
wait_for_completion(&priv->finished);
- return 0;
}
static struct spi_driver spi_slave_time_driver = {
diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c
index 6c44dda9ee8c..843be803696b 100644
--- a/drivers/spi/spi-st-ssc4.c
+++ b/drivers/spi/spi-st-ssc4.c
@@ -17,7 +17,6 @@
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
@@ -171,11 +170,6 @@ static int spi_st_transfer_one(struct spi_master *master,
return t->len;
}
-static void spi_st_cleanup(struct spi_device *spi)
-{
- gpio_free(spi->cs_gpio);
-}
-
/* the spi->mode bits understood by this driver: */
#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH)
static int spi_st_setup(struct spi_device *spi)
@@ -183,29 +177,17 @@ static int spi_st_setup(struct spi_device *spi)
struct spi_st *spi_st = spi_master_get_devdata(spi->master);
u32 spi_st_clk, sscbrg, var;
u32 hz = spi->max_speed_hz;
- int cs = spi->cs_gpio;
- int ret;
if (!hz) {
dev_err(&spi->dev, "max_speed_hz unspecified\n");
return -EINVAL;
}
- if (!gpio_is_valid(cs)) {
- dev_err(&spi->dev, "%d is not a valid gpio\n", cs);
+ if (!spi->cs_gpiod) {
+ dev_err(&spi->dev, "no valid gpio assigned\n");
return -EINVAL;
}
- ret = gpio_request(cs, dev_name(&spi->dev));
- if (ret) {
- dev_err(&spi->dev, "could not request gpio:%d\n", cs);
- return ret;
- }
-
- ret = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH);
- if (ret)
- goto out_free_gpio;
-
spi_st_clk = clk_get_rate(spi_st->clk);
/* Set SSC_BRF */
@@ -213,8 +195,7 @@ static int spi_st_setup(struct spi_device *spi)
if (sscbrg < 0x07 || sscbrg > BIT(16)) {
dev_err(&spi->dev,
"baudrate %d outside valid range %d\n", sscbrg, hz);
- ret = -EINVAL;
- goto out_free_gpio;
+ return -EINVAL;
}
spi_st->baud = spi_st_clk / (2 * sscbrg);
@@ -263,10 +244,6 @@ static int spi_st_setup(struct spi_device *spi)
readl_relaxed(spi_st->base + SSC_RBUF);
return 0;
-
-out_free_gpio:
- gpio_free(cs);
- return ret;
}
/* Interrupt fired when TX shift register becomes empty */
@@ -309,11 +286,11 @@ static int spi_st_probe(struct platform_device *pdev)
master->dev.of_node = np;
master->mode_bits = MODEBITS;
master->setup = spi_st_setup;
- master->cleanup = spi_st_cleanup;
master->transfer_one = spi_st_transfer_one;
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
master->auto_runtime_pm = true;
master->bus_num = pdev->id;
+ master->use_gpio_descriptors = true;
spi_st = spi_master_get_devdata(master);
spi_st->clk = devm_clk_get(&pdev->dev, "ssc");
diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c
index 7fc24505a72c..a6adc20f6862 100644
--- a/drivers/spi/spi-stm32.c
+++ b/drivers/spi/spi-stm32.c
@@ -763,7 +763,7 @@ static irqreturn_t stm32f4_spi_irq_event(int irq, void *dev_id)
if (!spi->cur_usedma && (spi->cur_comm == SPI_SIMPLEX_TX ||
spi->cur_comm == SPI_3WIRE_TX)) {
/* OVR flag shouldn't be handled for TX only mode */
- sr &= ~STM32F4_SPI_SR_OVR | STM32F4_SPI_SR_RXNE;
+ sr &= ~(STM32F4_SPI_SR_OVR | STM32F4_SPI_SR_RXNE);
mask |= STM32F4_SPI_SR_TXE;
}
diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c
index 1fdfc6e6691d..6000d0761206 100644
--- a/drivers/spi/spi-sun4i.c
+++ b/drivers/spi/spi-sun4i.c
@@ -280,7 +280,7 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
* SPI_CLK = MOD_CLK / (2 ^ (cdr + 1))
* Or we can use CDR2, which is calculated with the formula:
* SPI_CLK = MOD_CLK / (2 * (cdr + 1))
- * Wether we use the former or the latter is set through the
+ * Whether we use the former or the latter is set through the
* DRS bit.
*
* First try CDR2, and if we can't reach the expected
diff --git a/drivers/spi/spi-sunplus-sp7021.c b/drivers/spi/spi-sunplus-sp7021.c
new file mode 100644
index 000000000000..f989f7b99296
--- /dev/null
+++ b/drivers/spi/spi-sunplus-sp7021.c
@@ -0,0 +1,584 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2021 Sunplus Inc.
+// Author: Li-hao Kuo <lhjeff911@gmail.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+
+#define SP7021_DATA_RDY_REG 0x0044
+#define SP7021_SLAVE_DMA_CTRL_REG 0x0048
+#define SP7021_SLAVE_DMA_LENGTH_REG 0x004c
+#define SP7021_SLAVE_DMA_ADDR_REG 0x004c
+
+#define SP7021_SLAVE_DATA_RDY BIT(0)
+#define SP7021_SLAVE_SW_RST BIT(1)
+#define SP7021_SLA_DMA_W_INT BIT(8)
+#define SP7021_SLAVE_CLR_INT BIT(8)
+#define SP7021_SLAVE_DMA_EN BIT(0)
+#define SP7021_SLAVE_DMA_RW BIT(6)
+#define SP7021_SLAVE_DMA_CMD GENMASK(3, 2)
+
+#define SP7021_FIFO_REG 0x0034
+#define SP7021_SPI_STATUS_REG 0x0038
+#define SP7021_SPI_CONFIG_REG 0x003c
+#define SP7021_INT_BUSY_REG 0x004c
+#define SP7021_DMA_CTRL_REG 0x0050
+
+#define SP7021_SPI_START_FD BIT(0)
+#define SP7021_FD_SW_RST BIT(1)
+#define SP7021_TX_EMP_FLAG BIT(2)
+#define SP7021_RX_EMP_FLAG BIT(4)
+#define SP7021_RX_FULL_FLAG BIT(5)
+#define SP7021_FINISH_FLAG BIT(6)
+
+#define SP7021_TX_CNT_MASK GENMASK(11, 8)
+#define SP7021_RX_CNT_MASK GENMASK(15, 12)
+#define SP7021_TX_LEN_MASK GENMASK(23, 16)
+#define SP7021_GET_LEN_MASK GENMASK(31, 24)
+#define SP7021_SET_TX_LEN GENMASK(23, 16)
+#define SP7021_SET_XFER_LEN GENMASK(31, 24)
+
+#define SP7021_CPOL_FD BIT(0)
+#define SP7021_CPHA_R BIT(1)
+#define SP7021_CPHA_W BIT(2)
+#define SP7021_LSB_SEL BIT(4)
+#define SP7021_CS_POR BIT(5)
+#define SP7021_FD_SEL BIT(6)
+
+#define SP7021_RX_UNIT GENMASK(8, 7)
+#define SP7021_TX_UNIT GENMASK(10, 9)
+#define SP7021_TX_EMP_FLAG_MASK BIT(11)
+#define SP7021_RX_FULL_FLAG_MASK BIT(14)
+#define SP7021_FINISH_FLAG_MASK BIT(15)
+#define SP7021_CLEAN_RW_BYTE GENMASK(10, 7)
+#define SP7021_CLEAN_FLUG_MASK GENMASK(15, 11)
+#define SP7021_CLK_MASK GENMASK(31, 16)
+
+#define SP7021_INT_BYPASS BIT(3)
+#define SP7021_CLR_MASTER_INT BIT(6)
+
+#define SP7021_SPI_DATA_SIZE (255)
+#define SP7021_FIFO_DATA_LEN (16)
+
+enum {
+ SP7021_MASTER_MODE = 0,
+ SP7021_SLAVE_MODE = 1,
+};
+
+struct sp7021_spi_ctlr {
+ struct device *dev;
+ struct spi_controller *ctlr;
+ void __iomem *m_base;
+ void __iomem *s_base;
+ u32 xfer_conf;
+ int mode;
+ int m_irq;
+ int s_irq;
+ struct clk *spi_clk;
+ struct reset_control *rstc;
+ // irq spin lock
+ spinlock_t lock;
+ // data xfer lock
+ struct mutex buf_lock;
+ struct completion isr_done;
+ struct completion slave_isr;
+ unsigned int rx_cur_len;
+ unsigned int tx_cur_len;
+ unsigned int data_unit;
+ const u8 *tx_buf;
+ u8 *rx_buf;
+};
+
+static irqreturn_t sp7021_spi_slave_irq(int irq, void *dev)
+{
+ struct sp7021_spi_ctlr *pspim = dev;
+ unsigned int data_status;
+
+ data_status = readl(pspim->s_base + SP7021_DATA_RDY_REG);
+ data_status |= SP7021_SLAVE_CLR_INT;
+ writel(data_status , pspim->s_base + SP7021_DATA_RDY_REG);
+ complete(&pspim->slave_isr);
+ return IRQ_HANDLED;
+}
+
+static int sp7021_spi_slave_abort(struct spi_controller *ctlr)
+{
+ struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr);
+
+ complete(&pspim->slave_isr);
+ complete(&pspim->isr_done);
+ return 0;
+}
+
+static int sp7021_spi_slave_tx(struct spi_device *spi, struct spi_transfer *xfer)
+{
+ struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(spi->controller);
+ u32 value;
+
+ reinit_completion(&pspim->slave_isr);
+ value = SP7021_SLAVE_DMA_EN | SP7021_SLAVE_DMA_RW | FIELD_PREP(SP7021_SLAVE_DMA_CMD, 3);
+ writel(value, pspim->s_base + SP7021_SLAVE_DMA_CTRL_REG);
+ writel(xfer->len, pspim->s_base + SP7021_SLAVE_DMA_LENGTH_REG);
+ writel(xfer->tx_dma, pspim->s_base + SP7021_SLAVE_DMA_ADDR_REG);
+ value = readl(pspim->s_base + SP7021_DATA_RDY_REG);
+ value |= SP7021_SLAVE_DATA_RDY;
+ writel(value, pspim->s_base + SP7021_DATA_RDY_REG);
+ if (wait_for_completion_interruptible(&pspim->isr_done)) {
+ dev_err(&spi->dev, "%s() wait_for_completion err\n", __func__);
+ return -EINTR;
+ }
+ return 0;
+}
+
+static int sp7021_spi_slave_rx(struct spi_device *spi, struct spi_transfer *xfer)
+{
+ struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(spi->controller);
+ u32 value;
+
+ reinit_completion(&pspim->isr_done);
+ value = SP7021_SLAVE_DMA_EN | FIELD_PREP(SP7021_SLAVE_DMA_CMD, 3);
+ writel(value, pspim->s_base + SP7021_SLAVE_DMA_CTRL_REG);
+ writel(xfer->len, pspim->s_base + SP7021_SLAVE_DMA_LENGTH_REG);
+ writel(xfer->rx_dma, pspim->s_base + SP7021_SLAVE_DMA_ADDR_REG);
+ if (wait_for_completion_interruptible(&pspim->isr_done)) {
+ dev_err(&spi->dev, "%s() wait_for_completion err\n", __func__);
+ return -EINTR;
+ }
+ writel(SP7021_SLAVE_SW_RST, pspim->s_base + SP7021_SLAVE_DMA_CTRL_REG);
+ return 0;
+}
+
+static void sp7021_spi_master_rb(struct sp7021_spi_ctlr *pspim, unsigned int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ pspim->rx_buf[pspim->rx_cur_len] =
+ readl(pspim->m_base + SP7021_FIFO_REG);
+ pspim->rx_cur_len++;
+ }
+}
+
+static void sp7021_spi_master_wb(struct sp7021_spi_ctlr *pspim, unsigned int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ writel(pspim->tx_buf[pspim->tx_cur_len],
+ pspim->m_base + SP7021_FIFO_REG);
+ pspim->tx_cur_len++;
+ }
+}
+
+static irqreturn_t sp7021_spi_master_irq(int irq, void *dev)
+{
+ struct sp7021_spi_ctlr *pspim = dev;
+ unsigned int tx_cnt, total_len;
+ unsigned int tx_len, rx_cnt;
+ unsigned int fd_status;
+ bool isrdone = false;
+ u32 value;
+
+ fd_status = readl(pspim->m_base + SP7021_SPI_STATUS_REG);
+ tx_cnt = FIELD_GET(SP7021_TX_CNT_MASK, fd_status);
+ tx_len = FIELD_GET(SP7021_TX_LEN_MASK, fd_status);
+ total_len = FIELD_GET(SP7021_GET_LEN_MASK, fd_status);
+
+ if ((fd_status & SP7021_TX_EMP_FLAG) && (fd_status & SP7021_RX_EMP_FLAG) && total_len == 0)
+ return IRQ_NONE;
+
+ if (tx_len == 0 && total_len == 0)
+ return IRQ_NONE;
+
+ spin_lock_irq(&pspim->lock);
+
+ rx_cnt = FIELD_GET(SP7021_RX_CNT_MASK, fd_status);
+ if (fd_status & SP7021_RX_FULL_FLAG)
+ rx_cnt = pspim->data_unit;
+
+ tx_cnt = min(tx_len - pspim->tx_cur_len, pspim->data_unit - tx_cnt);
+ dev_dbg(pspim->dev, "fd_st=0x%x rx_c:%d tx_c:%d tx_l:%d",
+ fd_status, rx_cnt, tx_cnt, tx_len);
+
+ if (rx_cnt > 0)
+ sp7021_spi_master_rb(pspim, rx_cnt);
+ if (tx_cnt > 0)
+ sp7021_spi_master_wb(pspim, tx_cnt);
+
+ fd_status = readl(pspim->m_base + SP7021_SPI_STATUS_REG);
+ tx_len = FIELD_GET(SP7021_TX_LEN_MASK, fd_status);
+ total_len = FIELD_GET(SP7021_GET_LEN_MASK, fd_status);
+
+ if (fd_status & SP7021_FINISH_FLAG || tx_len == pspim->tx_cur_len) {
+ while (total_len != pspim->rx_cur_len) {
+ fd_status = readl(pspim->m_base + SP7021_SPI_STATUS_REG);
+ total_len = FIELD_GET(SP7021_GET_LEN_MASK, fd_status);
+ if (fd_status & SP7021_RX_FULL_FLAG)
+ rx_cnt = pspim->data_unit;
+ else
+ rx_cnt = FIELD_GET(SP7021_RX_CNT_MASK, fd_status);
+
+ if (rx_cnt > 0)
+ sp7021_spi_master_rb(pspim, rx_cnt);
+ }
+ value = readl(pspim->m_base + SP7021_INT_BUSY_REG);
+ value |= SP7021_CLR_MASTER_INT;
+ writel(value, pspim->m_base + SP7021_INT_BUSY_REG);
+ writel(SP7021_FINISH_FLAG, pspim->m_base + SP7021_SPI_STATUS_REG);
+ isrdone = true;
+ }
+
+ if (isrdone)
+ complete(&pspim->isr_done);
+ spin_unlock_irq(&pspim->lock);
+ return IRQ_HANDLED;
+}
+
+static void sp7021_prep_transfer(struct spi_controller *ctlr, struct spi_device *spi)
+{
+ struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr);
+
+ pspim->tx_cur_len = 0;
+ pspim->rx_cur_len = 0;
+ pspim->data_unit = SP7021_FIFO_DATA_LEN;
+}
+
+// preliminary set CS, CPOL, CPHA and LSB
+static int sp7021_spi_controller_prepare_message(struct spi_controller *ctlr,
+ struct spi_message *msg)
+{
+ struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr);
+ struct spi_device *s = msg->spi;
+ u32 valus, rs = 0;
+
+ valus = readl(pspim->m_base + SP7021_SPI_STATUS_REG);
+ valus |= SP7021_FD_SW_RST;
+ writel(valus, pspim->m_base + SP7021_SPI_STATUS_REG);
+ rs |= SP7021_FD_SEL;
+ if (s->mode & SPI_CPOL)
+ rs |= SP7021_CPOL_FD;
+
+ if (s->mode & SPI_LSB_FIRST)
+ rs |= SP7021_LSB_SEL;
+
+ if (s->mode & SPI_CS_HIGH)
+ rs |= SP7021_CS_POR;
+
+ if (s->mode & SPI_CPHA)
+ rs |= SP7021_CPHA_R;
+ else
+ rs |= SP7021_CPHA_W;
+
+ rs |= FIELD_PREP(SP7021_TX_UNIT, 0) | FIELD_PREP(SP7021_RX_UNIT, 0);
+ pspim->xfer_conf = rs;
+ if (pspim->xfer_conf & SP7021_CPOL_FD)
+ writel(pspim->xfer_conf, pspim->m_base + SP7021_SPI_CONFIG_REG);
+
+ return 0;
+}
+
+static void sp7021_spi_setup_clk(struct spi_controller *ctlr, struct spi_transfer *xfer)
+{
+ struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr);
+ u32 clk_rate, clk_sel, div;
+
+ clk_rate = clk_get_rate(pspim->spi_clk);
+ div = max(2U, clk_rate / xfer->speed_hz);
+
+ clk_sel = (div / 2) - 1;
+ pspim->xfer_conf &= ~SP7021_CLK_MASK;
+ pspim->xfer_conf |= FIELD_PREP(SP7021_CLK_MASK, clk_sel);
+ writel(pspim->xfer_conf, pspim->m_base + SP7021_SPI_CONFIG_REG);
+}
+
+static int sp7021_spi_master_transfer_one(struct spi_controller *ctlr, struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr);
+ unsigned long timeout = msecs_to_jiffies(1000);
+ unsigned int xfer_cnt, xfer_len, last_len;
+ unsigned int i, len_temp;
+ u32 reg_temp;
+
+ xfer_cnt = xfer->len / SP7021_SPI_DATA_SIZE;
+ last_len = xfer->len % SP7021_SPI_DATA_SIZE;
+
+ for (i = 0; i <= xfer_cnt; i++) {
+ mutex_lock(&pspim->buf_lock);
+ sp7021_prep_transfer(ctlr, spi);
+ sp7021_spi_setup_clk(ctlr, xfer);
+ reinit_completion(&pspim->isr_done);
+
+ if (i == xfer_cnt)
+ xfer_len = last_len;
+ else
+ xfer_len = SP7021_SPI_DATA_SIZE;
+
+ pspim->tx_buf = xfer->tx_buf + i * SP7021_SPI_DATA_SIZE;
+ pspim->rx_buf = xfer->rx_buf + i * SP7021_SPI_DATA_SIZE;
+
+ if (pspim->tx_cur_len < xfer_len) {
+ len_temp = min(pspim->data_unit, xfer_len);
+ sp7021_spi_master_wb(pspim, len_temp);
+ }
+ reg_temp = readl(pspim->m_base + SP7021_SPI_CONFIG_REG);
+ reg_temp &= ~SP7021_CLEAN_RW_BYTE;
+ reg_temp &= ~SP7021_CLEAN_FLUG_MASK;
+ reg_temp |= SP7021_FD_SEL | SP7021_FINISH_FLAG_MASK |
+ SP7021_TX_EMP_FLAG_MASK | SP7021_RX_FULL_FLAG_MASK |
+ FIELD_PREP(SP7021_TX_UNIT, 0) | FIELD_PREP(SP7021_RX_UNIT, 0);
+ writel(reg_temp, pspim->m_base + SP7021_SPI_CONFIG_REG);
+
+ reg_temp = FIELD_PREP(SP7021_SET_TX_LEN, xfer_len) |
+ FIELD_PREP(SP7021_SET_XFER_LEN, xfer_len) |
+ SP7021_SPI_START_FD;
+ writel(reg_temp, pspim->m_base + SP7021_SPI_STATUS_REG);
+
+ if (!wait_for_completion_interruptible_timeout(&pspim->isr_done, timeout)) {
+ dev_err(&spi->dev, "wait_for_completion err\n");
+ mutex_unlock(&pspim->buf_lock);
+ return -ETIMEDOUT;
+ }
+
+ reg_temp = readl(pspim->m_base + SP7021_SPI_STATUS_REG);
+ if (reg_temp & SP7021_FINISH_FLAG) {
+ writel(SP7021_FINISH_FLAG, pspim->m_base + SP7021_SPI_STATUS_REG);
+ writel(readl(pspim->m_base + SP7021_SPI_CONFIG_REG) &
+ SP7021_CLEAN_FLUG_MASK, pspim->m_base + SP7021_SPI_CONFIG_REG);
+ }
+
+ if (pspim->xfer_conf & SP7021_CPOL_FD)
+ writel(pspim->xfer_conf, pspim->m_base + SP7021_SPI_CONFIG_REG);
+
+ mutex_unlock(&pspim->buf_lock);
+ }
+ return 0;
+}
+
+static int sp7021_spi_slave_transfer_one(struct spi_controller *ctlr, struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr);
+ struct device *dev = pspim->dev;
+ int ret;
+
+ if (xfer->tx_buf && !xfer->rx_buf) {
+ xfer->tx_dma = dma_map_single(dev, (void *)xfer->tx_buf,
+ xfer->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, xfer->tx_dma))
+ return -ENOMEM;
+ ret = sp7021_spi_slave_tx(spi, xfer);
+ dma_unmap_single(dev, xfer->tx_dma, xfer->len, DMA_TO_DEVICE);
+ } else if (xfer->rx_buf && !xfer->tx_buf) {
+ xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, xfer->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, xfer->rx_dma))
+ return -ENOMEM;
+ ret = sp7021_spi_slave_rx(spi, xfer);
+ dma_unmap_single(dev, xfer->rx_dma, xfer->len, DMA_FROM_DEVICE);
+ } else {
+ dev_dbg(&ctlr->dev, "%s() wrong command\n", __func__);
+ return -EINVAL;
+ }
+
+ spi_finalize_current_transfer(ctlr);
+ return ret;
+}
+
+static void sp7021_spi_disable_unprepare(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
+static void sp7021_spi_reset_control_assert(void *data)
+{
+ reset_control_assert(data);
+}
+
+static int sp7021_spi_controller_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sp7021_spi_ctlr *pspim;
+ struct spi_controller *ctlr;
+ int mode, ret;
+
+ pdev->id = of_alias_get_id(pdev->dev.of_node, "sp_spi");
+
+ if (device_property_read_bool(dev, "spi-slave"))
+ mode = SP7021_SLAVE_MODE;
+ else
+ mode = SP7021_MASTER_MODE;
+
+ if (mode == SP7021_SLAVE_MODE)
+ ctlr = devm_spi_alloc_slave(dev, sizeof(*pspim));
+ else
+ ctlr = devm_spi_alloc_master(dev, sizeof(*pspim));
+ if (!ctlr)
+ return -ENOMEM;
+ device_set_node(&ctlr->dev, dev_fwnode(dev));
+ ctlr->bus_num = pdev->id;
+ ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
+ ctlr->auto_runtime_pm = true;
+ ctlr->prepare_message = sp7021_spi_controller_prepare_message;
+ if (mode == SP7021_SLAVE_MODE) {
+ ctlr->transfer_one = sp7021_spi_slave_transfer_one;
+ ctlr->slave_abort = sp7021_spi_slave_abort;
+ ctlr->flags = SPI_CONTROLLER_HALF_DUPLEX;
+ } else {
+ ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
+ ctlr->min_speed_hz = 40000;
+ ctlr->max_speed_hz = 25000000;
+ ctlr->use_gpio_descriptors = true;
+ ctlr->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX;
+ ctlr->transfer_one = sp7021_spi_master_transfer_one;
+ }
+ platform_set_drvdata(pdev, ctlr);
+ pspim = spi_controller_get_devdata(ctlr);
+ pspim->mode = mode;
+ pspim->ctlr = ctlr;
+ pspim->dev = dev;
+ spin_lock_init(&pspim->lock);
+ mutex_init(&pspim->buf_lock);
+ init_completion(&pspim->isr_done);
+ init_completion(&pspim->slave_isr);
+
+ pspim->m_base = devm_platform_ioremap_resource_byname(pdev, "master");
+ if (IS_ERR(pspim->m_base))
+ return dev_err_probe(dev, PTR_ERR(pspim->m_base), "m_base get fail\n");
+
+ pspim->s_base = devm_platform_ioremap_resource_byname(pdev, "slave");
+ if (IS_ERR(pspim->s_base))
+ return dev_err_probe(dev, PTR_ERR(pspim->s_base), "s_base get fail\n");
+
+ pspim->m_irq = platform_get_irq_byname(pdev, "master_risc");
+ if (pspim->m_irq < 0)
+ return pspim->m_irq;
+
+ pspim->s_irq = platform_get_irq_byname(pdev, "slave_risc");
+ if (pspim->s_irq < 0)
+ return pspim->s_irq;
+
+ pspim->spi_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(pspim->spi_clk))
+ return dev_err_probe(dev, PTR_ERR(pspim->spi_clk), "clk get fail\n");
+
+ pspim->rstc = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(pspim->rstc))
+ return dev_err_probe(dev, PTR_ERR(pspim->rstc), "rst get fail\n");
+
+ ret = clk_prepare_enable(pspim->spi_clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable clk\n");
+
+ ret = devm_add_action_or_reset(dev, sp7021_spi_disable_unprepare, pspim->spi_clk);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(pspim->rstc);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to deassert reset\n");
+
+ ret = devm_add_action_or_reset(dev, sp7021_spi_reset_control_assert, pspim->rstc);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(dev, pspim->m_irq, sp7021_spi_master_irq,
+ IRQF_TRIGGER_RISING, pdev->name, pspim);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(dev, pspim->s_irq, sp7021_spi_slave_irq,
+ IRQF_TRIGGER_RISING, pdev->name, pspim);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(dev);
+ ret = spi_register_controller(ctlr);
+ if (ret) {
+ pm_runtime_disable(dev);
+ return dev_err_probe(dev, ret, "spi_register_master fail\n");
+ }
+ return 0;
+}
+
+static int sp7021_spi_controller_remove(struct platform_device *pdev)
+{
+ struct spi_controller *ctlr = dev_get_drvdata(&pdev->dev);
+
+ spi_unregister_controller(ctlr);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ return 0;
+}
+
+static int __maybe_unused sp7021_spi_controller_suspend(struct device *dev)
+{
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr);
+
+ return reset_control_assert(pspim->rstc);
+}
+
+static int __maybe_unused sp7021_spi_controller_resume(struct device *dev)
+{
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr);
+
+ reset_control_deassert(pspim->rstc);
+ return clk_prepare_enable(pspim->spi_clk);
+}
+
+#ifdef CONFIG_PM
+static int sp7021_spi_runtime_suspend(struct device *dev)
+{
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr);
+
+ return reset_control_assert(pspim->rstc);
+}
+
+static int sp7021_spi_runtime_resume(struct device *dev)
+{
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr);
+
+ return reset_control_deassert(pspim->rstc);
+}
+#endif
+
+static const struct dev_pm_ops sp7021_spi_pm_ops = {
+ SET_RUNTIME_PM_OPS(sp7021_spi_runtime_suspend,
+ sp7021_spi_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(sp7021_spi_controller_suspend,
+ sp7021_spi_controller_resume)
+};
+
+static const struct of_device_id sp7021_spi_controller_ids[] = {
+ { .compatible = "sunplus,sp7021-spi" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sp7021_spi_controller_ids);
+
+static struct platform_driver sp7021_spi_controller_driver = {
+ .probe = sp7021_spi_controller_probe,
+ .remove = sp7021_spi_controller_remove,
+ .driver = {
+ .name = "sunplus,sp7021-spi-controller",
+ .of_match_table = sp7021_spi_controller_ids,
+ .pm = &sp7021_spi_pm_ops,
+ },
+};
+module_platform_driver(sp7021_spi_controller_driver);
+
+MODULE_AUTHOR("Li-hao Kuo <lhjeff911@gmail.com>");
+MODULE_DESCRIPTION("Sunplus SPI controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index e9de1d958bbd..8f345247a8c3 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -1352,6 +1352,10 @@ static int tegra_spi_probe(struct platform_device *pdev)
tspi->phys = r->start;
spi_irq = platform_get_irq(pdev, 0);
+ if (spi_irq < 0) {
+ ret = spi_irq;
+ goto exit_free_master;
+ }
tspi->irq = spi_irq;
tspi->clk = devm_clk_get(&pdev->dev, "spi");
diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c
index 2a03739a0c60..80c3787deea9 100644
--- a/drivers/spi/spi-tegra20-slink.c
+++ b/drivers/spi/spi-tegra20-slink.c
@@ -1006,14 +1006,8 @@ static int tegra_slink_probe(struct platform_device *pdev)
struct resource *r;
int ret, spi_irq;
const struct tegra_slink_chip_data *cdata = NULL;
- const struct of_device_id *match;
- match = of_match_device(tegra_slink_of_match, &pdev->dev);
- if (!match) {
- dev_err(&pdev->dev, "Error: No device match found\n");
- return -ENODEV;
- }
- cdata = match->data;
+ cdata = of_device_get_match_data(&pdev->dev);
master = spi_alloc_master(&pdev->dev, sizeof(*tspi));
if (!master) {
diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c
index ce1bdb4767ea..66f647f32876 100644
--- a/drivers/spi/spi-tegra210-quad.c
+++ b/drivers/spi/spi-tegra210-quad.c
@@ -21,6 +21,8 @@
#include <linux/of_device.h>
#include <linux/reset.h>
#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/property.h>
#define QSPI_COMMAND1 0x000
#define QSPI_BIT_LENGTH(x) (((x) & 0x1f) << 0)
@@ -119,11 +121,40 @@
#define QSPI_NUM_DUMMY_CYCLE(x) (((x) & 0xff) << 0)
#define QSPI_DUMMY_CYCLES_MAX 0xff
+#define QSPI_CMB_SEQ_CMD 0x19c
+#define QSPI_COMMAND_VALUE_SET(X) (((x) & 0xFF) << 0)
+
+#define QSPI_CMB_SEQ_CMD_CFG 0x1a0
+#define QSPI_COMMAND_X1_X2_X4(x) (((x) & 0x3) << 13)
+#define QSPI_COMMAND_X1_X2_X4_MASK (0x03 << 13)
+#define QSPI_COMMAND_SDR_DDR BIT(12)
+#define QSPI_COMMAND_SIZE_SET(x) (((x) & 0xFF) << 0)
+
+#define QSPI_GLOBAL_CONFIG 0X1a4
+#define QSPI_CMB_SEQ_EN BIT(0)
+
+#define QSPI_CMB_SEQ_ADDR 0x1a8
+#define QSPI_ADDRESS_VALUE_SET(X) (((x) & 0xFFFF) << 0)
+
+#define QSPI_CMB_SEQ_ADDR_CFG 0x1ac
+#define QSPI_ADDRESS_X1_X2_X4(x) (((x) & 0x3) << 13)
+#define QSPI_ADDRESS_X1_X2_X4_MASK (0x03 << 13)
+#define QSPI_ADDRESS_SDR_DDR BIT(12)
+#define QSPI_ADDRESS_SIZE_SET(x) (((x) & 0xFF) << 0)
+
#define DATA_DIR_TX BIT(0)
#define DATA_DIR_RX BIT(1)
#define QSPI_DMA_TIMEOUT (msecs_to_jiffies(1000))
#define DEFAULT_QSPI_DMA_BUF_LEN (64 * 1024)
+#define CMD_TRANSFER 0
+#define ADDR_TRANSFER 1
+#define DATA_TRANSFER 2
+
+struct tegra_qspi_soc_data {
+ bool has_dma;
+ bool cmb_xfer_capable;
+};
struct tegra_qspi_client_data {
int tx_clk_tap_delay;
@@ -137,7 +168,6 @@ struct tegra_qspi {
spinlock_t lock;
struct clk *clk;
- struct reset_control *rst;
void __iomem *base;
phys_addr_t phys;
unsigned int irq;
@@ -185,6 +215,7 @@ struct tegra_qspi {
u32 *tx_dma_buf;
dma_addr_t tx_dma_phys;
struct dma_async_tx_descriptor *tx_dma_desc;
+ const struct tegra_qspi_soc_data *soc_data;
};
static inline u32 tegra_qspi_readl(struct tegra_qspi *tqspi, unsigned long offset)
@@ -767,7 +798,7 @@ static u32 tegra_qspi_setup_transfer_one(struct spi_device *spi, struct spi_tran
u32 tx_tap = 0, rx_tap = 0;
int req_mode;
- if (speed != tqspi->cur_speed) {
+ if (!has_acpi_companion(tqspi->dev) && speed != tqspi->cur_speed) {
clk_set_rate(tqspi->clk, speed);
tqspi->cur_speed = speed;
}
@@ -875,16 +906,16 @@ static int tegra_qspi_start_transfer_one(struct spi_device *spi,
static struct tegra_qspi_client_data *tegra_qspi_parse_cdata_dt(struct spi_device *spi)
{
struct tegra_qspi_client_data *cdata;
- struct device_node *slave_np = spi->dev.of_node;
cdata = devm_kzalloc(&spi->dev, sizeof(*cdata), GFP_KERNEL);
if (!cdata)
return NULL;
- of_property_read_u32(slave_np, "nvidia,tx-clk-tap-delay",
- &cdata->tx_clk_tap_delay);
- of_property_read_u32(slave_np, "nvidia,rx-clk-tap-delay",
- &cdata->rx_clk_tap_delay);
+ device_property_read_u32(&spi->dev, "nvidia,tx-clk-tap-delay",
+ &cdata->tx_clk_tap_delay);
+ device_property_read_u32(&spi->dev, "nvidia,rx-clk-tap-delay",
+ &cdata->rx_clk_tap_delay);
+
return cdata;
}
@@ -906,7 +937,6 @@ static int tegra_qspi_setup(struct spi_device *spi)
cdata = tegra_qspi_parse_cdata_dt(spi);
spi->controller_data = cdata;
}
-
spin_lock_irqsave(&tqspi->lock, flags);
/* keep default cs state to inactive */
@@ -948,9 +978,8 @@ static void tegra_qspi_handle_error(struct tegra_qspi *tqspi)
dev_err(tqspi->dev, "error in transfer, fifo status 0x%08x\n", tqspi->status_reg);
tegra_qspi_dump_regs(tqspi);
tegra_qspi_flush_fifos(tqspi, true);
- reset_control_assert(tqspi->rst);
- udelay(2);
- reset_control_deassert(tqspi->rst);
+ if (device_reset(tqspi->dev) < 0)
+ dev_warn_once(tqspi->dev, "device reset failed\n");
}
static void tegra_qspi_transfer_end(struct spi_device *spi)
@@ -966,19 +995,179 @@ static void tegra_qspi_transfer_end(struct spi_device *spi)
tegra_qspi_writel(tqspi, tqspi->def_command1_reg, QSPI_COMMAND1);
}
-static int tegra_qspi_transfer_one_message(struct spi_master *master, struct spi_message *msg)
+static u32 tegra_qspi_cmd_config(bool is_ddr, u8 bus_width, u8 len)
+{
+ u32 cmd_config = 0;
+
+ /* Extract Command configuration and value */
+ if (is_ddr)
+ cmd_config |= QSPI_COMMAND_SDR_DDR;
+ else
+ cmd_config &= ~QSPI_COMMAND_SDR_DDR;
+
+ cmd_config |= QSPI_COMMAND_X1_X2_X4(bus_width);
+ cmd_config |= QSPI_COMMAND_SIZE_SET((len * 8) - 1);
+
+ return cmd_config;
+}
+
+static u32 tegra_qspi_addr_config(bool is_ddr, u8 bus_width, u8 len)
+{
+ u32 addr_config = 0;
+
+ /* Extract Address configuration and value */
+ is_ddr = 0; //Only SDR mode supported
+ bus_width = 0; //X1 mode
+
+ if (is_ddr)
+ addr_config |= QSPI_ADDRESS_SDR_DDR;
+ else
+ addr_config &= ~QSPI_ADDRESS_SDR_DDR;
+
+ addr_config |= QSPI_ADDRESS_X1_X2_X4(bus_width);
+ addr_config |= QSPI_ADDRESS_SIZE_SET((len * 8) - 1);
+
+ return addr_config;
+}
+
+static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi,
+ struct spi_message *msg)
+{
+ bool is_first_msg = true;
+ struct spi_transfer *xfer;
+ struct spi_device *spi = msg->spi;
+ u8 transfer_phase = 0;
+ u32 cmd1 = 0, dma_ctl = 0;
+ int ret = 0;
+ u32 address_value = 0;
+ u32 cmd_config = 0, addr_config = 0;
+ u8 cmd_value = 0, val = 0;
+
+ /* Enable Combined sequence mode */
+ val = tegra_qspi_readl(tqspi, QSPI_GLOBAL_CONFIG);
+ val |= QSPI_CMB_SEQ_EN;
+ tegra_qspi_writel(tqspi, val, QSPI_GLOBAL_CONFIG);
+ /* Process individual transfer list */
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ switch (transfer_phase) {
+ case CMD_TRANSFER:
+ /* X1 SDR mode */
+ cmd_config = tegra_qspi_cmd_config(false, 0,
+ xfer->len);
+ cmd_value = *((const u8 *)(xfer->tx_buf));
+ break;
+ case ADDR_TRANSFER:
+ /* X1 SDR mode */
+ addr_config = tegra_qspi_addr_config(false, 0,
+ xfer->len);
+ address_value = *((const u32 *)(xfer->tx_buf));
+ break;
+ case DATA_TRANSFER:
+ /* Program Command, Address value in register */
+ tegra_qspi_writel(tqspi, cmd_value, QSPI_CMB_SEQ_CMD);
+ tegra_qspi_writel(tqspi, address_value,
+ QSPI_CMB_SEQ_ADDR);
+ /* Program Command and Address config in register */
+ tegra_qspi_writel(tqspi, cmd_config,
+ QSPI_CMB_SEQ_CMD_CFG);
+ tegra_qspi_writel(tqspi, addr_config,
+ QSPI_CMB_SEQ_ADDR_CFG);
+
+ reinit_completion(&tqspi->xfer_completion);
+ cmd1 = tegra_qspi_setup_transfer_one(spi, xfer,
+ is_first_msg);
+ ret = tegra_qspi_start_transfer_one(spi, xfer,
+ cmd1);
+
+ if (ret < 0) {
+ dev_err(tqspi->dev, "Failed to start transfer-one: %d\n",
+ ret);
+ return ret;
+ }
+
+ is_first_msg = false;
+ ret = wait_for_completion_timeout
+ (&tqspi->xfer_completion,
+ QSPI_DMA_TIMEOUT);
+
+ if (WARN_ON(ret == 0)) {
+ dev_err(tqspi->dev, "QSPI Transfer failed with timeout: %d\n",
+ ret);
+ if (tqspi->is_curr_dma_xfer &&
+ (tqspi->cur_direction & DATA_DIR_TX))
+ dmaengine_terminate_all
+ (tqspi->tx_dma_chan);
+
+ if (tqspi->is_curr_dma_xfer &&
+ (tqspi->cur_direction & DATA_DIR_RX))
+ dmaengine_terminate_all
+ (tqspi->rx_dma_chan);
+
+ /* Abort transfer by resetting pio/dma bit */
+ if (!tqspi->is_curr_dma_xfer) {
+ cmd1 = tegra_qspi_readl
+ (tqspi,
+ QSPI_COMMAND1);
+ cmd1 &= ~QSPI_PIO;
+ tegra_qspi_writel
+ (tqspi, cmd1,
+ QSPI_COMMAND1);
+ } else {
+ dma_ctl = tegra_qspi_readl
+ (tqspi,
+ QSPI_DMA_CTL);
+ dma_ctl &= ~QSPI_DMA_EN;
+ tegra_qspi_writel(tqspi, dma_ctl,
+ QSPI_DMA_CTL);
+ }
+
+ /* Reset controller if timeout happens */
+ if (device_reset(tqspi->dev) < 0)
+ dev_warn_once(tqspi->dev,
+ "device reset failed\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ if (tqspi->tx_status || tqspi->rx_status) {
+ dev_err(tqspi->dev, "QSPI Transfer failed\n");
+ tqspi->tx_status = 0;
+ tqspi->rx_status = 0;
+ ret = -EIO;
+ goto exit;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ goto exit;
+ }
+ msg->actual_length += xfer->len;
+ transfer_phase++;
+ }
+
+exit:
+ msg->status = ret;
+
+ return ret;
+}
+
+static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi,
+ struct spi_message *msg)
{
- struct tegra_qspi *tqspi = spi_master_get_devdata(master);
struct spi_device *spi = msg->spi;
struct spi_transfer *transfer;
bool is_first_msg = true;
- int ret;
+ int ret = 0, val = 0;
msg->status = 0;
msg->actual_length = 0;
tqspi->tx_status = 0;
tqspi->rx_status = 0;
+ /* Disable Combined sequence mode */
+ val = tegra_qspi_readl(tqspi, QSPI_GLOBAL_CONFIG);
+ val &= ~QSPI_CMB_SEQ_EN;
+ tegra_qspi_writel(tqspi, val, QSPI_GLOBAL_CONFIG);
list_for_each_entry(transfer, &msg->transfers, transfer_list) {
struct spi_transfer *xfer = transfer;
u8 dummy_bytes = 0;
@@ -1016,7 +1205,6 @@ static int tegra_qspi_transfer_one_message(struct spi_master *master, struct spi
goto complete_xfer;
}
- is_first_msg = false;
ret = wait_for_completion_timeout(&tqspi->xfer_completion,
QSPI_DMA_TIMEOUT);
if (WARN_ON(ret == 0)) {
@@ -1061,7 +1249,48 @@ complete_xfer:
ret = 0;
exit:
msg->status = ret;
+
+ return ret;
+}
+
+static bool tegra_qspi_validate_cmb_seq(struct tegra_qspi *tqspi,
+ struct spi_message *msg)
+{
+ int transfer_count = 0;
+ struct spi_transfer *xfer;
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ transfer_count++;
+ }
+ if (!tqspi->soc_data->cmb_xfer_capable || transfer_count != 3)
+ return false;
+ xfer = list_first_entry(&msg->transfers, typeof(*xfer),
+ transfer_list);
+ if (xfer->len > 2)
+ return false;
+ xfer = list_next_entry(xfer, transfer_list);
+ if (xfer->len > 4 || xfer->len < 3)
+ return false;
+ xfer = list_next_entry(xfer, transfer_list);
+ if (!tqspi->soc_data->has_dma || xfer->len > (QSPI_FIFO_DEPTH << 2))
+ return false;
+
+ return true;
+}
+
+static int tegra_qspi_transfer_one_message(struct spi_master *master,
+ struct spi_message *msg)
+{
+ struct tegra_qspi *tqspi = spi_master_get_devdata(master);
+ int ret;
+
+ if (tegra_qspi_validate_cmb_seq(tqspi, msg))
+ ret = tegra_qspi_combined_seq_xfer(tqspi, msg);
+ else
+ ret = tegra_qspi_non_combined_seq_xfer(tqspi, msg);
+
spi_finalize_current_message(master);
+
return ret;
}
@@ -1193,15 +1422,58 @@ static irqreturn_t tegra_qspi_isr_thread(int irq, void *context_data)
return handle_dma_based_xfer(tqspi);
}
+static struct tegra_qspi_soc_data tegra210_qspi_soc_data = {
+ .has_dma = true,
+ .cmb_xfer_capable = false,
+};
+
+static struct tegra_qspi_soc_data tegra186_qspi_soc_data = {
+ .has_dma = true,
+ .cmb_xfer_capable = true,
+};
+
+static struct tegra_qspi_soc_data tegra234_qspi_soc_data = {
+ .has_dma = false,
+ .cmb_xfer_capable = true,
+};
+
static const struct of_device_id tegra_qspi_of_match[] = {
- { .compatible = "nvidia,tegra210-qspi", },
- { .compatible = "nvidia,tegra186-qspi", },
- { .compatible = "nvidia,tegra194-qspi", },
+ {
+ .compatible = "nvidia,tegra210-qspi",
+ .data = &tegra210_qspi_soc_data,
+ }, {
+ .compatible = "nvidia,tegra186-qspi",
+ .data = &tegra186_qspi_soc_data,
+ }, {
+ .compatible = "nvidia,tegra194-qspi",
+ .data = &tegra186_qspi_soc_data,
+ }, {
+ .compatible = "nvidia,tegra234-qspi",
+ .data = &tegra234_qspi_soc_data,
+ },
{}
};
MODULE_DEVICE_TABLE(of, tegra_qspi_of_match);
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id tegra_qspi_acpi_match[] = {
+ {
+ .id = "NVDA1213",
+ .driver_data = (kernel_ulong_t)&tegra210_qspi_soc_data,
+ }, {
+ .id = "NVDA1313",
+ .driver_data = (kernel_ulong_t)&tegra186_qspi_soc_data,
+ }, {
+ .id = "NVDA1413",
+ .driver_data = (kernel_ulong_t)&tegra234_qspi_soc_data,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, tegra_qspi_acpi_match);
+#endif
+
static int tegra_qspi_probe(struct platform_device *pdev)
{
struct spi_master *master;
@@ -1233,6 +1505,7 @@ static int tegra_qspi_probe(struct platform_device *pdev)
tqspi->dev = &pdev->dev;
spin_lock_init(&tqspi->lock);
+ tqspi->soc_data = device_get_match_data(&pdev->dev);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tqspi->base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(tqspi->base))
@@ -1240,20 +1513,18 @@ static int tegra_qspi_probe(struct platform_device *pdev)
tqspi->phys = r->start;
qspi_irq = platform_get_irq(pdev, 0);
+ if (qspi_irq < 0)
+ return qspi_irq;
tqspi->irq = qspi_irq;
- tqspi->clk = devm_clk_get(&pdev->dev, "qspi");
- if (IS_ERR(tqspi->clk)) {
- ret = PTR_ERR(tqspi->clk);
- dev_err(&pdev->dev, "failed to get clock: %d\n", ret);
- return ret;
- }
+ if (!has_acpi_companion(tqspi->dev)) {
+ tqspi->clk = devm_clk_get(&pdev->dev, "qspi");
+ if (IS_ERR(tqspi->clk)) {
+ ret = PTR_ERR(tqspi->clk);
+ dev_err(&pdev->dev, "failed to get clock: %d\n", ret);
+ return ret;
+ }
- tqspi->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
- if (IS_ERR(tqspi->rst)) {
- ret = PTR_ERR(tqspi->rst);
- dev_err(&pdev->dev, "failed to get reset control: %d\n", ret);
- return ret;
}
tqspi->max_buf_size = QSPI_FIFO_DEPTH << 2;
@@ -1277,9 +1548,8 @@ static int tegra_qspi_probe(struct platform_device *pdev)
goto exit_pm_disable;
}
- reset_control_assert(tqspi->rst);
- udelay(2);
- reset_control_deassert(tqspi->rst);
+ if (device_reset(tqspi->dev) < 0)
+ dev_warn_once(tqspi->dev, "device reset failed\n");
tqspi->def_command1_reg = QSPI_M_S | QSPI_CS_SW_HW | QSPI_CS_SW_VAL;
tegra_qspi_writel(tqspi, tqspi->def_command1_reg, QSPI_COMMAND1);
@@ -1358,6 +1628,9 @@ static int __maybe_unused tegra_qspi_runtime_suspend(struct device *dev)
struct spi_master *master = dev_get_drvdata(dev);
struct tegra_qspi *tqspi = spi_master_get_devdata(master);
+ /* Runtime pm disabled with ACPI */
+ if (has_acpi_companion(tqspi->dev))
+ return 0;
/* flush all write which are in PPSB queue by reading back */
tegra_qspi_readl(tqspi, QSPI_COMMAND1);
@@ -1372,6 +1645,9 @@ static int __maybe_unused tegra_qspi_runtime_resume(struct device *dev)
struct tegra_qspi *tqspi = spi_master_get_devdata(master);
int ret;
+ /* Runtime pm disabled with ACPI */
+ if (has_acpi_companion(tqspi->dev))
+ return 0;
ret = clk_prepare_enable(tqspi->clk);
if (ret < 0)
dev_err(tqspi->dev, "failed to enable clock: %d\n", ret);
@@ -1389,6 +1665,7 @@ static struct platform_driver tegra_qspi_driver = {
.name = "tegra-qspi",
.pm = &tegra_qspi_pm_ops,
.of_match_table = tegra_qspi_of_match,
+ .acpi_match_table = ACPI_PTR(tegra_qspi_acpi_match),
},
.probe = tegra_qspi_probe,
.remove = tegra_qspi_remove,
diff --git a/drivers/spi/spi-tle62x0.c b/drivers/spi/spi-tle62x0.c
index f8ad0709d015..a565352f6381 100644
--- a/drivers/spi/spi-tle62x0.c
+++ b/drivers/spi/spi-tle62x0.c
@@ -288,7 +288,7 @@ static int tle62x0_probe(struct spi_device *spi)
return ret;
}
-static int tle62x0_remove(struct spi_device *spi)
+static void tle62x0_remove(struct spi_device *spi)
{
struct tle62x0_state *st = spi_get_drvdata(spi);
int ptr;
@@ -298,7 +298,6 @@ static int tle62x0_remove(struct spi_device *spi)
device_remove_file(&spi->dev, &dev_attr_status_show);
kfree(st);
- return 0;
}
static struct spi_driver tle62x0_driver = {
diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c
index 8c4615b76339..dfaa1d79a78b 100644
--- a/drivers/spi/spi-topcliff-pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -103,6 +103,7 @@
static int use_dma = 1;
struct pch_spi_dma_ctrl {
+ struct pci_dev *dma_dev;
struct dma_async_tx_descriptor *desc_tx;
struct dma_async_tx_descriptor *desc_rx;
struct pch_dma_slave param_tx;
@@ -876,8 +877,7 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw)
if (!chan) {
dev_err(&data->master->dev,
"ERROR: dma_request_channel FAILS(Tx)\n");
- data->use_dma = 0;
- return;
+ goto out;
}
dma->chan_tx = chan;
@@ -893,10 +893,15 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw)
"ERROR: dma_request_channel FAILS(Rx)\n");
dma_release_channel(dma->chan_tx);
dma->chan_tx = NULL;
- data->use_dma = 0;
- return;
+ goto out;
}
dma->chan_rx = chan;
+
+ dma->dma_dev = dma_dev;
+ return;
+out:
+ pci_dev_put(dma_dev);
+ data->use_dma = 0;
}
static void pch_spi_release_dma(struct pch_spi_data *data)
@@ -912,6 +917,8 @@ static void pch_spi_release_dma(struct pch_spi_data *data)
dma_release_channel(dma->chan_rx);
dma->chan_rx = NULL;
}
+
+ pci_dev_put(dma->dma_dev);
}
static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
index 328b6559bb19..2b5afae8ff7f 100644
--- a/drivers/spi/spi-zynqmp-gqspi.c
+++ b/drivers/spi/spi-zynqmp-gqspi.c
@@ -1172,7 +1172,10 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
goto clk_dis_all;
}
- dma_set_mask(&pdev->dev, DMA_BIT_MASK(44));
+ ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(44));
+ if (ret)
+ goto clk_dis_all;
+
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
ctlr->num_chipselect = GQSPI_DEFAULT_NUM_CS;
ctlr->mem_ops = &zynqmp_qspi_mem_ops;
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d96082dc3340..c4dd1200fe99 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -18,7 +18,6 @@
#include <linux/mod_devicetable.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
-#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
@@ -144,7 +143,7 @@ static ssize_t spi_statistics_##name##_show(struct spi_statistics *stat, \
unsigned long flags; \
ssize_t len; \
spin_lock_irqsave(&stat->lock, flags); \
- len = sprintf(buf, format_string, stat->field); \
+ len = sysfs_emit(buf, format_string "\n", stat->field); \
spin_unlock_irqrestore(&stat->lock, flags); \
return len; \
} \
@@ -404,15 +403,8 @@ static void spi_remove(struct device *dev)
{
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
- if (sdrv->remove) {
- int ret;
-
- ret = sdrv->remove(to_spi_device(dev));
- if (ret)
- dev_warn(dev,
- "Failed to unbind driver (%pe), ignoring\n",
- ERR_PTR(ret));
- }
+ if (sdrv->remove)
+ sdrv->remove(to_spi_device(dev));
dev_pm_domain_detach(dev, true);
}
@@ -532,7 +524,7 @@ static DEFINE_MUTEX(board_lock);
*
* Return: a pointer to the new device, or NULL.
*/
-static struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
+struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
{
struct spi_device *spi;
@@ -549,7 +541,6 @@ static struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
spi->dev.parent = &ctlr->dev;
spi->dev.bus = &spi_bus_type;
spi->dev.release = spidev_release;
- spi->cs_gpio = -ENOENT;
spi->mode = ctlr->buswidth_override_bits;
spin_lock_init(&spi->statistics.lock);
@@ -557,6 +548,7 @@ static struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
device_initialize(&spi->dev);
return spi;
}
+EXPORT_SYMBOL_GPL(spi_alloc_device);
static void spi_dev_set_name(struct spi_device *spi)
{
@@ -612,11 +604,8 @@ static int __spi_add_device(struct spi_device *spi)
return -ENODEV;
}
- /* Descriptors take precedence */
if (ctlr->cs_gpiods)
spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select];
- else if (ctlr->cs_gpios)
- spi->cs_gpio = ctlr->cs_gpios[spi->chip_select];
/*
* Drivers may modify this initial i/o setup, but will
@@ -652,7 +641,7 @@ static int __spi_add_device(struct spi_device *spi)
*
* Return: 0 on success; negative errno on failure
*/
-static int spi_add_device(struct spi_device *spi)
+int spi_add_device(struct spi_device *spi)
{
struct spi_controller *ctlr = spi->controller;
struct device *dev = ctlr->dev.parent;
@@ -673,6 +662,7 @@ static int spi_add_device(struct spi_device *spi)
mutex_unlock(&ctlr->add_lock);
return status;
}
+EXPORT_SYMBOL_GPL(spi_add_device);
static int spi_add_device_locked(struct spi_device *spi)
{
@@ -936,48 +926,40 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
* Avoid calling into the driver (or doing delays) if the chip select
* isn't actually changing from the last time this was called.
*/
- if (!force && (spi->controller->last_cs_enable == enable) &&
+ if (!force && ((enable && spi->controller->last_cs == spi->chip_select) ||
+ (!enable && spi->controller->last_cs != spi->chip_select)) &&
(spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH)))
return;
trace_spi_set_cs(spi, activate);
- spi->controller->last_cs_enable = enable;
+ spi->controller->last_cs = enable ? spi->chip_select : -1;
spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
- if ((spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) ||
- !spi->controller->set_cs_timing) && !activate) {
+ if ((spi->cs_gpiod || !spi->controller->set_cs_timing) && !activate) {
spi_delay_exec(&spi->cs_hold, NULL);
}
if (spi->mode & SPI_CS_HIGH)
enable = !enable;
- if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio)) {
+ if (spi->cs_gpiod) {
if (!(spi->mode & SPI_NO_CS)) {
- if (spi->cs_gpiod) {
- /*
- * Historically ACPI has no means of the GPIO polarity and
- * thus the SPISerialBus() resource defines it on the per-chip
- * basis. In order to avoid a chain of negations, the GPIO
- * polarity is considered being Active High. Even for the cases
- * when _DSD() is involved (in the updated versions of ACPI)
- * the GPIO CS polarity must be defined Active High to avoid
- * ambiguity. That's why we use enable, that takes SPI_CS_HIGH
- * into account.
- */
- if (has_acpi_companion(&spi->dev))
- gpiod_set_value_cansleep(spi->cs_gpiod, !enable);
- else
- /* Polarity handled by GPIO library */
- gpiod_set_value_cansleep(spi->cs_gpiod, activate);
- } else {
- /*
- * Invert the enable line, as active low is
- * default for SPI.
- */
- gpio_set_value_cansleep(spi->cs_gpio, !enable);
- }
+ /*
+ * Historically ACPI has no means of the GPIO polarity and
+ * thus the SPISerialBus() resource defines it on the per-chip
+ * basis. In order to avoid a chain of negations, the GPIO
+ * polarity is considered being Active High. Even for the cases
+ * when _DSD() is involved (in the updated versions of ACPI)
+ * the GPIO CS polarity must be defined Active High to avoid
+ * ambiguity. That's why we use enable, that takes SPI_CS_HIGH
+ * into account.
+ */
+ if (has_acpi_companion(&spi->dev))
+ gpiod_set_value_cansleep(spi->cs_gpiod, !enable);
+ else
+ /* Polarity handled by GPIO library */
+ gpiod_set_value_cansleep(spi->cs_gpiod, activate);
}
/* Some SPI masters need both GPIO CS & slave_select */
if ((spi->controller->flags & SPI_MASTER_GPIO_SS) &&
@@ -987,8 +969,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
spi->controller->set_cs(spi, !enable);
}
- if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) ||
- !spi->controller->set_cs_timing) {
+ if (spi->cs_gpiod || !spi->controller->set_cs_timing) {
if (activate)
spi_delay_exec(&spi->cs_setup, NULL);
else
@@ -1019,10 +1000,10 @@ int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
int i, ret;
if (vmalloced_buf || kmap_buf) {
- desc_len = min_t(unsigned int, max_seg_size, PAGE_SIZE);
+ desc_len = min_t(unsigned long, max_seg_size, PAGE_SIZE);
sgs = DIV_ROUND_UP(len + offset_in_page(buf), desc_len);
} else if (virt_addr_valid(buf)) {
- desc_len = min_t(unsigned int, max_seg_size, ctlr->max_dma_len);
+ desc_len = min_t(size_t, max_seg_size, ctlr->max_dma_len);
sgs = DIV_ROUND_UP(len, desc_len);
} else {
return -EINVAL;
@@ -2318,8 +2299,50 @@ struct acpi_spi_lookup {
int irq;
u8 bits_per_word;
u8 chip_select;
+ int n;
+ int index;
};
+static int acpi_spi_count(struct acpi_resource *ares, void *data)
+{
+ struct acpi_resource_spi_serialbus *sb;
+ int *count = data;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
+ return 1;
+
+ sb = &ares->data.spi_serial_bus;
+ if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_SPI)
+ return 1;
+
+ *count = *count + 1;
+
+ return 1;
+}
+
+/**
+ * acpi_spi_count_resources - Count the number of SpiSerialBus resources
+ * @adev: ACPI device
+ *
+ * Returns the number of SpiSerialBus resources in the ACPI-device's
+ * resource-list; or a negative error code.
+ */
+int acpi_spi_count_resources(struct acpi_device *adev)
+{
+ LIST_HEAD(r);
+ int count = 0;
+ int ret;
+
+ ret = acpi_dev_get_resources(adev, &r, acpi_spi_count, &count);
+ if (ret < 0)
+ return ret;
+
+ acpi_dev_free_resource_list(&r);
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(acpi_spi_count_resources);
+
static void acpi_spi_parse_apple_properties(struct acpi_device *dev,
struct acpi_spi_lookup *lookup)
{
@@ -2349,6 +2372,8 @@ static void acpi_spi_parse_apple_properties(struct acpi_device *dev,
lookup->mode |= SPI_CPHA;
}
+static struct spi_controller *acpi_spi_find_controller_by_adev(struct acpi_device *adev);
+
static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
{
struct acpi_spi_lookup *lookup = data;
@@ -2362,14 +2387,35 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
sb = &ares->data.spi_serial_bus;
if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_SPI) {
+ if (lookup->index != -1 && lookup->n++ != lookup->index)
+ return 1;
+
+ if (lookup->index == -1 && !ctlr)
+ return -ENODEV;
+
status = acpi_get_handle(NULL,
sb->resource_source.string_ptr,
&parent_handle);
- if (ACPI_FAILURE(status) ||
- ACPI_HANDLE(ctlr->dev.parent) != parent_handle)
+ if (ACPI_FAILURE(status))
return -ENODEV;
+ if (ctlr) {
+ if (ACPI_HANDLE(ctlr->dev.parent) != parent_handle)
+ return -ENODEV;
+ } else {
+ struct acpi_device *adev;
+
+ if (acpi_bus_get_device(parent_handle, &adev))
+ return -ENODEV;
+
+ ctlr = acpi_spi_find_controller_by_adev(adev);
+ if (!ctlr)
+ return -ENODEV;
+
+ lookup->ctlr = ctlr;
+ }
+
/*
* ACPI DeviceSelection numbering is handled by the
* host controller driver in Windows and can vary
@@ -2408,8 +2454,25 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
return 1;
}
-static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
- struct acpi_device *adev)
+/**
+ * acpi_spi_device_alloc - Allocate a spi device, and fill it in with ACPI information
+ * @ctlr: controller to which the spi device belongs
+ * @adev: ACPI Device for the spi device
+ * @index: Index of the spi resource inside the ACPI Node
+ *
+ * This should be used to allocate a new spi device from and ACPI Node.
+ * The caller is responsible for calling spi_add_device to register the spi device.
+ *
+ * If ctlr is set to NULL, the Controller for the spi device will be looked up
+ * using the resource.
+ * If index is set to -1, index is not used.
+ * Note: If index is -1, ctlr must be set.
+ *
+ * Return: a pointer to the new device, or ERR_PTR on error.
+ */
+struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
+ struct acpi_device *adev,
+ int index)
{
acpi_handle parent_handle = NULL;
struct list_head resource_list;
@@ -2417,12 +2480,13 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
struct spi_device *spi;
int ret;
- if (acpi_bus_get_status(adev) || !adev->status.present ||
- acpi_device_enumerated(adev))
- return AE_OK;
+ if (!ctlr && index == -1)
+ return ERR_PTR(-EINVAL);
lookup.ctlr = ctlr;
lookup.irq = -1;
+ lookup.index = index;
+ lookup.n = 0;
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list,
@@ -2431,26 +2495,25 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
if (ret < 0)
/* found SPI in _CRS but it points to another controller */
- return AE_OK;
+ return ERR_PTR(-ENODEV);
if (!lookup.max_speed_hz &&
ACPI_SUCCESS(acpi_get_parent(adev->handle, &parent_handle)) &&
- ACPI_HANDLE(ctlr->dev.parent) == parent_handle) {
+ ACPI_HANDLE(lookup.ctlr->dev.parent) == parent_handle) {
/* Apple does not use _CRS but nested devices for SPI slaves */
acpi_spi_parse_apple_properties(adev, &lookup);
}
if (!lookup.max_speed_hz)
- return AE_OK;
+ return ERR_PTR(-ENODEV);
- spi = spi_alloc_device(ctlr);
+ spi = spi_alloc_device(lookup.ctlr);
if (!spi) {
- dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n",
+ dev_err(&lookup.ctlr->dev, "failed to allocate SPI device for %s\n",
dev_name(&adev->dev));
- return AE_NO_MEMORY;
+ return ERR_PTR(-ENOMEM);
}
-
ACPI_COMPANION_SET(&spi->dev, adev);
spi->max_speed_hz = lookup.max_speed_hz;
spi->mode |= lookup.mode;
@@ -2458,6 +2521,27 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
spi->bits_per_word = lookup.bits_per_word;
spi->chip_select = lookup.chip_select;
+ return spi;
+}
+EXPORT_SYMBOL_GPL(acpi_spi_device_alloc);
+
+static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
+ struct acpi_device *adev)
+{
+ struct spi_device *spi;
+
+ if (acpi_bus_get_status(adev) || !adev->status.present ||
+ acpi_device_enumerated(adev))
+ return AE_OK;
+
+ spi = acpi_spi_device_alloc(ctlr, adev, -1);
+ if (IS_ERR(spi)) {
+ if (PTR_ERR(spi) == -ENOMEM)
+ return AE_NO_MEMORY;
+ else
+ return AE_OK;
+ }
+
acpi_set_modalias(adev, acpi_device_hid(adev), spi->modalias,
sizeof(spi->modalias));
@@ -2480,10 +2564,10 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
void *data, void **return_value)
{
+ struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
struct spi_controller *ctlr = data;
- struct acpi_device *adev;
- if (acpi_bus_get_device(handle, &adev))
+ if (!adev)
return AE_OK;
return acpi_register_spi_device(ctlr, adev);
@@ -2729,46 +2813,6 @@ struct spi_controller *__devm_spi_alloc_controller(struct device *dev,
}
EXPORT_SYMBOL_GPL(__devm_spi_alloc_controller);
-#ifdef CONFIG_OF
-static int of_spi_get_gpio_numbers(struct spi_controller *ctlr)
-{
- int nb, i, *cs;
- struct device_node *np = ctlr->dev.of_node;
-
- if (!np)
- return 0;
-
- nb = of_gpio_named_count(np, "cs-gpios");
- ctlr->num_chipselect = max_t(int, nb, ctlr->num_chipselect);
-
- /* Return error only for an incorrectly formed cs-gpios property */
- if (nb == 0 || nb == -ENOENT)
- return 0;
- else if (nb < 0)
- return nb;
-
- cs = devm_kcalloc(&ctlr->dev, ctlr->num_chipselect, sizeof(int),
- GFP_KERNEL);
- ctlr->cs_gpios = cs;
-
- if (!ctlr->cs_gpios)
- return -ENOMEM;
-
- for (i = 0; i < ctlr->num_chipselect; i++)
- cs[i] = -ENOENT;
-
- for (i = 0; i < nb; i++)
- cs[i] = of_get_named_gpio(np, "cs-gpios", i);
-
- return 0;
-}
-#else
-static int of_spi_get_gpio_numbers(struct spi_controller *ctlr)
-{
- return 0;
-}
-#endif
-
/**
* spi_get_gpio_descs() - grab chip select GPIOs for the master
* @ctlr: The SPI master to grab GPIO descriptors for
@@ -2953,22 +2997,15 @@ int spi_register_controller(struct spi_controller *ctlr)
*/
dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num);
- if (!spi_controller_is_slave(ctlr)) {
- if (ctlr->use_gpio_descriptors) {
- status = spi_get_gpio_descs(ctlr);
- if (status)
- goto free_bus_id;
- /*
- * A controller using GPIO descriptors always
- * supports SPI_CS_HIGH if need be.
- */
- ctlr->mode_bits |= SPI_CS_HIGH;
- } else {
- /* Legacy code path for GPIOs from DT */
- status = of_spi_get_gpio_numbers(ctlr);
- if (status)
- goto free_bus_id;
- }
+ if (!spi_controller_is_slave(ctlr) && ctlr->use_gpio_descriptors) {
+ status = spi_get_gpio_descs(ctlr);
+ if (status)
+ goto free_bus_id;
+ /*
+ * A controller using GPIO descriptors always
+ * supports SPI_CS_HIGH if need be.
+ */
+ ctlr->mode_bits |= SPI_CS_HIGH;
}
/*
@@ -2980,6 +3017,9 @@ int spi_register_controller(struct spi_controller *ctlr)
goto free_bus_id;
}
+ /* setting last_cs to -1 means no chip selected */
+ ctlr->last_cs = -1;
+
status = device_add(&ctlr->dev);
if (status < 0)
goto free_bus_id;
@@ -3457,12 +3497,6 @@ int spi_setup(struct spi_device *spi)
*/
bad_bits = spi->mode & ~(spi->controller->mode_bits | SPI_CS_WORD |
SPI_NO_TX | SPI_NO_RX);
- /*
- * Nothing prevents from working with active-high CS in case if it
- * is driven by GPIO.
- */
- if (gpio_is_valid(spi->cs_gpio))
- bad_bits &= ~SPI_CS_HIGH;
ugly_bits = bad_bits &
(SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL |
SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL);
@@ -3588,8 +3622,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
* cs_change is set for each transfer.
*/
if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) ||
- spi->cs_gpiod ||
- gpio_is_valid(spi->cs_gpio))) {
+ spi->cs_gpiod)) {
size_t maxsize;
int ret;
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index a5cceca8b82b..53a551714265 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -453,22 +453,29 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
dev_dbg(&spi->dev, "%d bits per word\n", tmp);
}
break;
- case SPI_IOC_WR_MAX_SPEED_HZ:
+ case SPI_IOC_WR_MAX_SPEED_HZ: {
+ u32 save;
+
retval = get_user(tmp, (__u32 __user *)arg);
- if (retval == 0) {
- u32 save = spi->max_speed_hz;
+ if (retval)
+ break;
+ if (tmp == 0) {
+ retval = -EINVAL;
+ break;
+ }
- spi->max_speed_hz = tmp;
- retval = spi_setup(spi);
- if (retval == 0) {
- spidev->speed_hz = tmp;
- dev_dbg(&spi->dev, "%d Hz (max)\n",
- spidev->speed_hz);
- }
- spi->max_speed_hz = save;
+ save = spi->max_speed_hz;
+
+ spi->max_speed_hz = tmp;
+ retval = spi_setup(spi);
+ if (retval == 0) {
+ spidev->speed_hz = tmp;
+ dev_dbg(&spi->dev, "%d Hz (max)\n", spidev->speed_hz);
}
- break;
+ spi->max_speed_hz = save;
+ break;
+ }
default:
/* segmented and/or full-duplex I/O request */
/* Check message and copy into scratch area */
@@ -803,7 +810,7 @@ static int spidev_probe(struct spi_device *spi)
return status;
}
-static int spidev_remove(struct spi_device *spi)
+static void spidev_remove(struct spi_device *spi)
{
struct spidev_data *spidev = spi_get_drvdata(spi);
@@ -820,8 +827,6 @@ static int spidev_remove(struct spi_device *spi)
if (spidev->users == 0)
kfree(spidev);
mutex_unlock(&device_list_lock);
-
- return 0;
}
static struct spi_driver spidev_spi_driver = {