summaryrefslogtreecommitdiffstats
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/Makefile8
-rw-r--r--drivers/spi/altera_spi.c247
-rw-r--r--drivers/spi/ath79_spi.c30
-rw-r--r--drivers/spi/atmel-quadspi.c19
-rw-r--r--drivers/spi/atmel_spi.c30
-rw-r--r--drivers/spi/atmel_spi.h5
-rw-r--r--drivers/spi/dspi_spi.c26
-rw-r--r--drivers/spi/gpio_spi.c29
-rw-r--r--drivers/spi/imx_spi.c32
-rw-r--r--drivers/spi/litex_spiflash.c242
-rw-r--r--drivers/spi/mvebu_spi.c18
-rw-r--r--drivers/spi/mxs_spi.c16
-rw-r--r--drivers/spi/omap3_spi.c27
-rw-r--r--drivers/spi/omap3_spi.h15
-rw-r--r--drivers/spi/spi-bitbang-txrx.h1
-rw-r--r--drivers/spi/spi-fsl-dspi.c656
-rw-r--r--drivers/spi/spi-fsl-qspi.c13
-rw-r--r--drivers/spi/spi-mem.c2
-rw-r--r--drivers/spi/spi-nxp-fspi.c1064
-rw-r--r--drivers/spi/spi-sifive.c522
-rw-r--r--drivers/spi/spi.c129
-rw-r--r--drivers/spi/stm32_spi.c639
-rw-r--r--drivers/spi/zynq_qspi.c613
24 files changed, 3968 insertions, 465 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 9d4b1cc4e3..445c756a38 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
menu "SPI drivers"
config SPI
@@ -13,10 +14,6 @@ config SPI_MEM
This extension is meant to simplify interaction with SPI memories
by providing a high-level interface to send memory-like commands.
-config DRIVER_SPI_ALTERA
- bool "Altera SPI Master driver"
- depends on NIOS2
-
config DRIVER_SPI_ATH79
bool "Atheros AR71XX/AR724X/AR913X/AR933X SPI controller driver"
depends on MACH_MIPS_ATH79
@@ -44,6 +41,13 @@ config DRIVER_SPI_FSL_QUADSPI
This controller does not support generic SPI messages. It only
supports the high-level SPI memory interface.
+config SPI_FSL_DSPI
+ bool "Freescale DSPI controller"
+ depends on ARCH_LAYERSCAPE
+ help
+ This enables support for the Freescale DSPI controller in master
+ mode. LS1021A and ColdFire platforms use the controller.
+
config DRIVER_SPI_GPIO
bool "GPIO SPI Master driver"
depends on GPIOLIB
@@ -64,13 +68,16 @@ config DRIVER_SPI_IMX_0_7
config DRIVER_SPI_IMX_2_3
bool
- depends on ARCH_IMX50 || ARCH_IMX51 || ARCH_IMX53 || ARCH_IMX6 || ARCH_IMX7 || ARCH_IMX8MQ
+ depends on ARCH_IMX50 || ARCH_IMX51 || ARCH_IMX53 || ARCH_IMX6 || ARCH_IMX7 || ARCH_IMX8M
default y
+config DRIVER_SPI_LITEX_SPIFLASH
+ bool "Litex SPIFLASH bitbang master driver"
+
config DRIVER_SPI_MXS
bool "i.MX (23,28) SPI Master driver"
depends on ARCH_IMX28
- depends on SPI
+ select STMP_DEVICE
config DRIVER_SPI_MVEBU
bool "Marvell MVEBU SoC SPI master driver"
@@ -88,6 +95,37 @@ config DRIVER_SPI_DSPI
This enables support for the Freescale DSPI controller in master
mode. VF610 platform uses the controller.
+config SPI_ZYNQ_QSPI
+ tristate "Xilinx Zynq QSPI controller"
+ depends on ARCH_ZYNQ
+ depends on SPI_MEM
+ help
+ This enables support for the Zynq Quad SPI controller in master mode.
+ This controller only supports SPI memory interface.
+
+config DRIVER_SPI_STM32
+ bool "STM32 SPI driver"
+ depends on ARCH_STM32MP || COMPILE_TEST
+ help
+ Enable the STM32 Serial Peripheral Interface (SPI) driver for STM32MP
+ SoCs.
+
+config SPI_NXP_FLEXSPI
+ tristate "NXP Flex SPI controller"
+ depends on ARCH_IMX8M || ARCH_IMX93 || COMPILE_TEST
+ help
+ This enables support for the Flex SPI controller in master mode.
+ Up to four slave devices can be connected on two buses with two
+ chipselects each.
+ This controller does not support generic SPI messages and only
+ supports the high-level SPI memory interface.
+
+config SPI_SIFIVE
+ tristate "SiFive SPI controller"
+ depends on SOC_SIFIVE || COMPILE_TEST
+ help
+ This exposes the SPI controller IP from SiFive.
+
endif
endmenu
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index c5d2de74e1..68a8c4e675 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -1,13 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SPI) += spi.o
obj-$(CONFIG_SPI_MEM) += spi-mem.o
obj-$(CONFIG_DRIVER_SPI_ATH79) += ath79_spi.o
obj-$(CONFIG_DRIVER_SPI_GPIO) += gpio_spi.o
obj-$(CONFIG_DRIVER_SPI_FSL_QUADSPI) += spi-fsl-qspi.o
obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o
+obj-$(CONFIG_DRIVER_SPI_LITEX_SPIFLASH) += litex_spiflash.o
obj-$(CONFIG_DRIVER_SPI_MVEBU) += mvebu_spi.o
obj-$(CONFIG_DRIVER_SPI_MXS) += mxs_spi.o
-obj-$(CONFIG_DRIVER_SPI_ALTERA) += altera_spi.o
obj-$(CONFIG_DRIVER_SPI_ATMEL) += atmel_spi.o
+obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
obj-$(CONFIG_DRIVER_SPI_OMAP3) += omap3_spi.o
obj-$(CONFIG_DRIVER_SPI_DSPI) += dspi_spi.o
+obj-$(CONFIG_SPI_ZYNQ_QSPI) += zynq_qspi.o
+obj-$(CONFIG_SPI_NXP_FLEXSPI) += spi-nxp-fspi.o
+obj-$(CONFIG_DRIVER_SPI_STM32) += stm32_spi.o
+obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o
diff --git a/drivers/spi/altera_spi.c b/drivers/spi/altera_spi.c
deleted file mode 100644
index 4506e2741d..0000000000
--- a/drivers/spi/altera_spi.c
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * (C) Copyright 2011 - Franck JULLIEN <elec4fun@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- */
-
-#include <common.h>
-#include <init.h>
-#include <driver.h>
-#include <spi/spi.h>
-#include <io.h>
-#include <asm/spi.h>
-#include <asm/nios2-io.h>
-#include <clock.h>
-
-static void altera_spi_cs_inactive(struct spi_device *spi);
-
-static int altera_spi_setup(struct spi_device *spi)
-{
- struct spi_master *master = spi->master;
- struct device_d spi_dev = spi->dev;
- struct altera_spi *altera_spi = container_of(master, struct altera_spi, master);
-
- if (spi->bits_per_word != altera_spi->databits) {
- dev_err(master->dev, " master doesn't support %d bits per word requested by %s\n",
- spi->bits_per_word, spi_dev.name);
- return -EINVAL;
- }
-
- if ((spi->mode & (SPI_CPHA | SPI_CPOL)) != altera_spi->mode) {
- dev_err(master->dev, " master doesn't support SPI_MODE%d requested by %s\n",
- spi->mode & (SPI_CPHA | SPI_CPOL), spi_dev.name);
- return -EINVAL;
- }
-
- if (spi->max_speed_hz < altera_spi->speed) {
- dev_err(master->dev, " frequency is too high for %s\n", spi_dev.name);
- return -EINVAL;
- }
-
- altera_spi_cs_inactive(spi);
-
- dev_dbg(master->dev, " mode 0x%08x, bits_per_word: %d, speed: %d\n",
- spi->mode, spi->bits_per_word, altera_spi->speed);
-
- return 0;
-}
-
-
-static unsigned int altera_spi_xchg_single(struct altera_spi *altera_spi, unsigned int data)
-{
- struct nios_spi *nios_spi = altera_spi->regs;
-
- while (!(readl(&nios_spi->status) & NIOS_SPI_TRDY));
- writel(data, &nios_spi->txdata);
-
- while (!(readl(&nios_spi->status) & NIOS_SPI_RRDY));
-
- return readl(&nios_spi->rxdata);
-}
-
-/*
- * When using SPI_CS_HIGH devices, only one device is allowed to be
- * connected to the Altera SPI master. This limitation is due to the
- * emulation of an active high CS by writing 0 to the slaveselect register
- * (this produce a '1' to all CS pins).
- */
-
-static void altera_spi_cs_active(struct spi_device *spi)
-{
- struct altera_spi *altera_spi = container_of(spi->master, struct altera_spi, master);
- struct nios_spi *nios_spi = altera_spi->regs;
- uint32_t tmp;
-
- if (spi->mode & SPI_CS_HIGH) {
- tmp = readw(&nios_spi->control);
- writew(tmp & ~NIOS_SPI_SSO, &nios_spi->control);
- writel(0, &nios_spi->slaveselect);
- } else {
- writel(1 << spi->chip_select, &nios_spi->slaveselect);
- tmp = readl(&nios_spi->control);
- writel(tmp | NIOS_SPI_SSO, &nios_spi->control);
- }
-}
-
-static void altera_spi_cs_inactive(struct spi_device *spi)
-{
- struct altera_spi *altera_spi = container_of(spi->master, struct altera_spi, master);
- struct nios_spi *nios_spi = altera_spi->regs;
- uint32_t tmp;
-
- if (spi->mode & SPI_CS_HIGH) {
- writel(1 << spi->chip_select, &nios_spi->slaveselect);
- tmp = readl(&nios_spi->control);
- writel(tmp | NIOS_SPI_SSO, &nios_spi->control);
- } else {
- tmp = readw(&nios_spi->control);
- writew(tmp & ~NIOS_SPI_SSO, &nios_spi->control);
- }
-}
-
-static unsigned altera_spi_do_xfer(struct spi_device *spi, struct spi_transfer *t)
-{
- struct altera_spi *altera_spi = container_of(spi->master, struct altera_spi, master);
- int word_len;
- unsigned retval = 0;
- u32 txval;
- u32 rxval;
-
- word_len = spi->bits_per_word;
-
- if (word_len <= 8) {
- const u8 *txbuf = t->tx_buf;
- u8 *rxbuf = t->rx_buf;
- int i = 0;
-
- while (i < t->len) {
- txval = txbuf ? txbuf[i] : 0;
- rxval = altera_spi_xchg_single(altera_spi, txval);
- if (rxbuf)
- rxbuf[i] = rxval;
- i++;
- retval++;
- }
- } else if (word_len <= 16) {
- const u16 *txbuf = t->tx_buf;
- u16 *rxbuf = t->rx_buf;
- int i = 0;
-
- while (i < t->len >> 1) {
- txval = txbuf ? txbuf[i] : 0;
- rxval = altera_spi_xchg_single(altera_spi, txval);
- if (rxbuf)
- rxbuf[i] = rxval;
- i++;
- retval += 2;
- }
- } else if (word_len <= 32) {
- const u32 *txbuf = t->tx_buf;
- u32 *rxbuf = t->rx_buf;
- int i = 0;
-
- while (i < t->len >> 2) {
- txval = txbuf ? txbuf[i] : 0;
- rxval = altera_spi_xchg_single(altera_spi, txval);
- if (rxbuf)
- rxbuf[i] = rxval;
- i++;
- retval += 4;
- }
- }
-
- return retval;
-}
-
-static int altera_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
-{
- struct altera_spi *altera_spi = container_of(spi->master, struct altera_spi, master);
- struct nios_spi *nios_spi = altera_spi->regs;
- struct spi_transfer *t;
- unsigned int cs_change;
- const int nsecs = 50;
-
- altera_spi_cs_active(spi);
-
- cs_change = 0;
-
- mesg->actual_length = 0;
-
- list_for_each_entry(t, &mesg->transfers, transfer_list) {
-
- if (cs_change) {
- ndelay(nsecs);
- altera_spi_cs_inactive(spi);
- ndelay(nsecs);
- altera_spi_cs_active(spi);
- }
-
- cs_change = t->cs_change;
-
- mesg->actual_length += altera_spi_do_xfer(spi, t);
-
- if (cs_change) {
- altera_spi_cs_active(spi);
- }
- }
-
- /* Wait the end of any pending transfer */
- while ((readl(&nios_spi->status) & NIOS_SPI_TMT) == 0);
-
- if (!cs_change)
- altera_spi_cs_inactive(spi);
-
- return 0;
-}
-
-static int altera_spi_probe(struct device_d *dev)
-{
- struct resource *iores;
- struct spi_master *master;
- struct altera_spi *altera_spi;
- struct spi_altera_master *pdata = dev->platform_data;
- struct nios_spi *nios_spi;
-
- altera_spi = xzalloc(sizeof(*altera_spi));
-
- master = &altera_spi->master;
- master->dev = dev;
-
- master->setup = altera_spi_setup;
- master->transfer = altera_spi_transfer;
- master->num_chipselect = pdata->num_chipselect;
- master->bus_num = pdata->bus_num;
-
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- altera_spi->regs = IOMEM(iores->start);
-
- altera_spi->databits = pdata->databits;
- altera_spi->speed = pdata->speed;
- altera_spi->mode = pdata->spi_mode;
-
- nios_spi = altera_spi->regs;
- writel(0, &nios_spi->slaveselect);
- writel(0, &nios_spi->control);
-
- spi_register_master(master);
-
- return 0;
-}
-
-static struct driver_d altera_spi_driver = {
- .name = "altera_spi",
- .probe = altera_spi_probe,
-};
-device_platform_driver(altera_spi_driver);
diff --git a/drivers/spi/ath79_spi.c b/drivers/spi/ath79_spi.c
index bb63d864ae..41a31ae922 100644
--- a/drivers/spi/ath79_spi.c
+++ b/drivers/spi/ath79_spi.c
@@ -1,19 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2013, 2014 Antony Pavlov <antonynpavlov@gmail.com>
*
* This file is part of barebox.
- * See file CREDITS for list of people who contributed to this project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <common.h>
@@ -56,7 +45,7 @@ static inline void ath79_spi_wr(struct ath79_spi *sp, u32 val, int reg)
__raw_writel(val, sp->regs + reg);
}
-static inline void setbits(struct ath79_spi *sp, int bits, int on)
+static inline void ath79_spi_setbits(struct ath79_spi *sp, int bits, int on)
{
/*
* We are the only user of SCSPTR so no locking is required.
@@ -83,14 +72,14 @@ static inline void setsck(struct spi_device *spi, int on)
{
struct ath79_spi *sc = ath79_spidev_to_sp(spi);
- setbits(sc, AR71XX_SPI_IOC_CLK, on);
+ ath79_spi_setbits(sc, AR71XX_SPI_IOC_CLK, on);
}
static inline void setmosi(struct spi_device *spi, int on)
{
struct ath79_spi *sc = ath79_spidev_to_sp(spi);
- setbits(sc, AR71XX_SPI_IOC_DO, on);
+ ath79_spi_setbits(sc, AR71XX_SPI_IOC_DO, on);
}
static inline u32 getmiso(struct spi_device *spi)
@@ -137,7 +126,7 @@ static inline void ath79_spi_chipselect(struct ath79_spi *sp, int chipselect)
static int ath79_spi_setup(struct spi_device *spi)
{
struct spi_master *master = spi->master;
- struct device_d spi_dev = spi->dev;
+ struct device spi_dev = spi->dev;
if (spi->bits_per_word != 8) {
dev_err(master->dev, "master doesn't support %d bits per word requested by %s\n",
@@ -231,7 +220,7 @@ static void ath79_spi_disable(struct ath79_spi *sp)
ath79_spi_wr(sp, 0, AR71XX_SPI_REG_FS);
}
-static int ath79_spi_probe(struct device_d *dev)
+static int ath79_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -249,7 +238,7 @@ static int ath79_spi_probe(struct device_d *dev)
master->num_chipselect = 3;
if (IS_ENABLED(CONFIG_OFDEVICE)) {
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
u32 num_cs;
int ret;
@@ -280,7 +269,7 @@ static int ath79_spi_probe(struct device_d *dev)
return 0;
}
-static void ath79_spi_remove(struct device_d *dev)
+static void ath79_spi_remove(struct device *dev)
{
struct ath79_spi *sp = dev->priv;
@@ -295,8 +284,9 @@ static __maybe_unused struct of_device_id ath79_spi_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, ath79_spi_dt_ids);
-static struct driver_d ath79_spi_driver = {
+static struct driver ath79_spi_driver = {
.name = "ath79-spi",
.probe = ath79_spi_probe,
.remove = ath79_spi_remove,
diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c
index 6799f95ea3..c680ee15a0 100644
--- a/drivers/spi/atmel-quadspi.c
+++ b/drivers/spi/atmel-quadspi.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Atmel QSPI Controller
*
@@ -21,22 +21,17 @@
#include <of_gpio.h>
#include <io.h>
#include <spi/spi.h>
-#include <mach/iomux.h>
-#include <mach/board.h>
-#include <mach/cpu.h>
+#include <mach/at91/iomux.h>
+#include <mach/at91/board.h>
+#include <mach/at91/cpu.h>
#include <linux/clk.h>
#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/spi/spi-mem.h>
#include <of_device.h>
#include <linux/iopoll.h>
-#define writel_relaxed writel
-#define readl_relaxed readl
-
/* QSPI register offsets */
#define QSPI_CR 0x0000 /* Control Register */
#define QSPI_MR 0x0004 /* Mode Register */
@@ -162,7 +157,6 @@ struct atmel_qspi {
void __iomem *mem;
struct clk *pclk;
struct clk *qspick;
- struct platform_device *pdev;
const struct atmel_qspi_caps *caps;
u32 mr;
};
@@ -419,7 +413,7 @@ static int atmel_qspi_init(struct atmel_qspi *aq)
return 0;
}
-static int atmel_qspi_probe(struct device_d *dev)
+static int atmel_qspi_probe(struct device *dev)
{
struct spi_controller *ctrl;
struct atmel_qspi *aq;
@@ -532,8 +526,9 @@ static const struct of_device_id atmel_qspi_dt_ids[] = {
},
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, atmel_qspi_dt_ids);
-static struct driver_d atmel_qspi_driver = {
+static struct driver atmel_qspi_driver = {
.name = "atmel_qspi",
.of_compatible = atmel_qspi_dt_ids,
.probe = atmel_qspi_probe,
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index 55bea79a5e..90f655dc3e 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Atmel AT32 and AT91 SPI Controllers
*
@@ -8,18 +9,6 @@
*
* based on atmel_spi.c from the linux kernel by:
* Copyright (C) 2006 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
*/
#include <common.h>
@@ -32,9 +21,9 @@
#include <of_gpio.h>
#include <io.h>
#include <spi/spi.h>
-#include <mach/iomux.h>
-#include <mach/board.h>
-#include <mach/cpu.h>
+#include <mach/at91/iomux.h>
+#include <mach/at91/board.h>
+#include <mach/at91/cpu.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -389,18 +378,18 @@ static void atmel_get_caps(struct atmel_spi *as)
unsigned int version;
version = atmel_get_version(as);
- dev_info(as->master.dev, "version: 0x%x\n", version);
+ dev_dbg(as->master.dev, "version: 0x%x\n", version);
as->caps.is_spi2 = version > 0x121;
}
-static int atmel_spi_probe(struct device_d *dev)
+static int atmel_spi_probe(struct device *dev)
{
struct resource *iores;
int ret = 0;
int i;
struct spi_master *master;
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
struct atmel_spi *as;
struct at91_spi_platform_data *pdata = dev->platform_data;
@@ -419,7 +408,7 @@ static int atmel_spi_probe(struct device_d *dev)
master->num_chipselect = pdata->num_chipselect;
as->cs_pins = pdata->chipselect;
} else {
- master->num_chipselect = of_gpio_named_count(node, "cs-gpios");
+ master->num_chipselect = of_gpio_count_csgpios(node);
as->cs_pins = xzalloc(sizeof(u32) * master->num_chipselect);
for (i = 0; i < master->num_chipselect; i++) {
@@ -485,8 +474,9 @@ const static __maybe_unused struct of_device_id atmel_spi_dt_ids[] = {
{ .compatible = "atmel,at91rm9200-spi" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, atmel_spi_dt_ids);
-static struct driver_d atmel_spi_driver = {
+static struct driver atmel_spi_driver = {
.name = "atmel_spi",
.probe = atmel_spi_probe,
.of_compatible = DRV_OF_COMPAT(atmel_spi_dt_ids),
diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h
index 7c4806981f..1f8cce7068 100644
--- a/drivers/spi/atmel_spi.h
+++ b/drivers/spi/atmel_spi.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Register definitions for Atmel Serial Peripheral Interface (SPI)
*
* Copyright (C) 2006 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __ATMEL_SPI_H__
#define __ATMEL_SPI_H__
diff --git a/drivers/spi/dspi_spi.c b/drivers/spi/dspi_spi.c
index 07b2f2c567..75addfd12c 100644
--- a/drivers/spi/dspi_spi.c
+++ b/drivers/spi/dspi_spi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Zodiac Inflight Innovation
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
@@ -5,12 +6,6 @@
* Based on drivers/spi/spi-fsl-dspi.c from Linux kernel
*
* Copyright 2013 Freescale Semiconductor, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
*/
#include <common.h>
@@ -26,8 +21,8 @@
#include <gpio.h>
#include <of_gpio.h>
#include <of_device.h>
-#include <mach/spi.h>
-#include <mach/generic.h>
+#include <mach/imx/spi.h>
+#include <mach/imx/generic.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <clock.h>
@@ -98,7 +93,7 @@ static struct fsl_dspi *to_dspi(struct spi_master *master)
return container_of(master, struct fsl_dspi, master);
}
-static void hz_to_spi_baud(struct device_d *dev,
+static void hz_to_spi_baud(struct device *dev,
char *pbr, char *br, int speed_hz,
unsigned long clkrate)
{
@@ -137,7 +132,7 @@ static void hz_to_spi_baud(struct device_d *dev,
}
}
-static void ns_delay_scale(struct device_d *dev,
+static void ns_delay_scale(struct device *dev,
char *psc, char *sc, int delay_ns,
unsigned long clkrate)
{
@@ -319,12 +314,12 @@ static int dspi_setup(struct spi_device *spi)
return 0;
}
-static int dspi_probe(struct device_d *dev)
+static int dspi_probe(struct device *dev)
{
struct resource *io;
struct fsl_dspi *dspi;
struct spi_master *master;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int ret = 0;
uint32_t bus_num = 0;
@@ -356,9 +351,9 @@ static int dspi_probe(struct device_d *dev)
else
master->bus_num = dev->id;
- of_property_read_u32(dev->device_node, "fsl,spi-cs-sck-delay",
+ of_property_read_u32(dev->of_node, "fsl,spi-cs-sck-delay",
&dspi->cs_sck_delay);
- of_property_read_u32(dev->device_node, "fsl,spi-sck-cs-delay",
+ of_property_read_u32(dev->of_node, "fsl,spi-sck-cs-delay",
&dspi->sck_cs_delay);
io = dev_request_mem_resource(dev, 0);
@@ -405,8 +400,9 @@ static const struct of_device_id dspi_dt_ids[] = {
{ .compatible = "fsl,vf610-dspi", .data = (void *)&vf610_data, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, dspi_dt_ids);
-static struct driver_d dspi_spi_driver = {
+static struct driver dspi_spi_driver = {
.name = "fsl-dspi",
.probe = dspi_probe,
.of_compatible = DRV_OF_COMPAT(dspi_dt_ids),
diff --git a/drivers/spi/gpio_spi.c b/drivers/spi/gpio_spi.c
index e37db756f8..e5664df3fe 100644
--- a/drivers/spi/gpio_spi.c
+++ b/drivers/spi/gpio_spi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* SPI master driver using generic bitbanged GPIO
*
@@ -5,16 +6,6 @@
*
* Based on Linux driver
* Copyright (C) 2006,2008 David Brownell
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <common.h>
@@ -135,9 +126,9 @@ static int gpio_spi_setup(struct spi_device *spi)
return 0;
}
-static int gpio_spi_of_probe(struct device_d *dev)
+static int gpio_spi_of_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct gpio_spi_pdata *pdata;
int n, sck;
@@ -145,12 +136,9 @@ static int gpio_spi_of_probe(struct device_d *dev)
return 0;
sck = of_get_named_gpio(np, "gpio-sck", 0);
- if (sck == -EPROBE_DEFER)
- return sck;
- if (!gpio_is_valid(sck)) {
- dev_err(dev, "missing mandatory SCK gpio\n");
- return sck;
- }
+ if (!gpio_is_valid(sck))
+ return dev_err_probe(dev, sck < 0 ? sck : -EINVAL,
+ "missing mandatory SCK gpio\n");
pdata = xzalloc(sizeof(*pdata));
pdata->sck = sck;
@@ -175,7 +163,7 @@ static int gpio_spi_of_probe(struct device_d *dev)
return 0;
}
-static int gpio_spi_probe(struct device_d *dev)
+static int gpio_spi_probe(struct device *dev)
{
struct gpio_spi *priv;
struct gpio_spi_pdata *pdata;
@@ -231,8 +219,9 @@ static struct of_device_id __maybe_unused gpio_spi_dt_ids[] = {
{ .compatible = "spi-gpio", },
{ }
};
+MODULE_DEVICE_TABLE(of, gpio_spi_dt_ids);
-static struct driver_d gpio_spi_driver = {
+static struct driver gpio_spi_driver = {
.name = "gpio-spi",
.probe = gpio_spi_probe,
.of_compatible = DRV_OF_COMPAT(gpio_spi_dt_ids),
diff --git a/drivers/spi/imx_spi.c b/drivers/spi/imx_spi.c
index 46e8955abb..5310a2715d 100644
--- a/drivers/spi/imx_spi.c
+++ b/drivers/spi/imx_spi.c
@@ -1,17 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2008 Sascha Hauer, Pengutronix
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
*/
#include <common.h>
@@ -26,8 +15,8 @@
#include <malloc.h>
#include <gpio.h>
#include <of_gpio.h>
-#include <mach/spi.h>
-#include <mach/generic.h>
+#include <mach/imx/spi.h>
+#include <mach/imx/generic.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <clock.h>
@@ -573,13 +562,13 @@ static __maybe_unused struct spi_imx_devtype_data spi_imx_devtype_data_2_3 = {
static int imx_spi_dt_probe(struct imx_spi *imx)
{
- struct device_node *node = imx->master.dev->device_node;
+ struct device_node *node = imx->master.dev->of_node;
int i;
if (!node)
return -ENODEV;
- imx->master.num_chipselect = of_gpio_named_count(node, "cs-gpios");
+ imx->master.num_chipselect = of_gpio_count_csgpios(node);
imx->cs_array = xzalloc(sizeof(u32) * imx->master.num_chipselect);
for (i = 0; i < imx->master.num_chipselect; i++)
@@ -588,7 +577,7 @@ static int imx_spi_dt_probe(struct imx_spi *imx)
return 0;
}
-static int imx_spi_probe(struct device_d *dev)
+static int imx_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -614,7 +603,7 @@ static int imx_spi_probe(struct device_d *dev)
master->num_chipselect = pdata->num_chipselect;
imx->cs_array = pdata->chipselect;
} else if (IS_ENABLED(CONFIG_OFDEVICE)) {
- ret = of_alias_get_id(dev->device_node, "spi");
+ ret = of_alias_get_id(dev->of_node, "spi");
if (ret < 0)
goto err_free;
master->bus_num = ret;
@@ -673,11 +662,16 @@ static __maybe_unused struct of_device_id imx_spi_dt_ids[] = {
.compatible = "fsl,imx51-ecspi",
.data = &spi_imx_devtype_data_2_3,
},
+ {
+ .compatible = "fsl,imx6ul-ecspi",
+ .data = &spi_imx_devtype_data_2_3,
+ },
#endif
{
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_spi_dt_ids);
static struct platform_device_id imx_spi_ids[] = {
#if IS_ENABLED(CONFIG_DRIVER_SPI_IMX_0_0)
@@ -703,7 +697,7 @@ static struct platform_device_id imx_spi_ids[] = {
}
};
-static struct driver_d imx_spi_driver = {
+static struct driver imx_spi_driver = {
.name = "imx_spi",
.probe = imx_spi_probe,
.of_compatible = DRV_OF_COMPAT(imx_spi_dt_ids),
diff --git a/drivers/spi/litex_spiflash.c b/drivers/spi/litex_spiflash.c
new file mode 100644
index 0000000000..58ce6ad5f5
--- /dev/null
+++ b/drivers/spi/litex_spiflash.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <spi/spi.h>
+#include <io.h>
+
+struct litex_spiflash_spi {
+ struct spi_master master;
+ void __iomem *regs;
+ u32 val;
+};
+
+#define SPIFLASH_BITBANG 0x0
+#define SPIFLASH_BB_MOSI BIT(0)
+#define SPIFLASH_BB_CLK BIT(1)
+#define SPIFLASH_BB_CSN BIT(2)
+#define SPIFLASH_BB_DIR BIT(3)
+
+#define SPIFLASH_MISO 0x4
+#define SPIFLASH_BITBANG_EN 0x8
+
+static inline u32 litex_spiflash_spi_rr(struct litex_spiflash_spi *sp, int reg)
+{
+ return readl(sp->regs + reg);
+}
+
+static inline void litex_spiflash_spi_wr(struct litex_spiflash_spi *sp, u32 val, int reg)
+{
+ writel(val, sp->regs + reg);
+}
+
+static inline void litex_setbits(struct litex_spiflash_spi *sp, int bits, int on)
+{
+ /*
+ * We are the only user of SCSPTR so no locking is required.
+ * Reading bit 2 and 0 in SCSPTR gives pin state as input.
+ * Writing the same bits sets the output value.
+ * This makes regular read-modify-write difficult so we
+ * use sp->val to keep track of the latest register value.
+ */
+
+ if (on)
+ sp->val |= bits;
+ else
+ sp->val &= ~bits;
+
+ litex_spiflash_spi_wr(sp, sp->val, SPIFLASH_BITBANG);
+}
+
+static inline struct litex_spiflash_spi *litex_spiflash_spidev_to_sp(struct spi_device *spi)
+{
+ return container_of(spi->master, struct litex_spiflash_spi, master);
+}
+
+static inline void setsck(struct spi_device *spi, int on)
+{
+ struct litex_spiflash_spi *sc = litex_spiflash_spidev_to_sp(spi);
+
+ litex_setbits(sc, SPIFLASH_BB_CLK, on);
+}
+
+static inline void setmosi(struct spi_device *spi, int on)
+{
+ struct litex_spiflash_spi *sc = litex_spiflash_spidev_to_sp(spi);
+
+ sc->val &= ~SPIFLASH_BB_DIR;
+ litex_setbits(sc, SPIFLASH_BB_MOSI, on);
+}
+
+static inline u32 getmiso(struct spi_device *spi)
+{
+ struct litex_spiflash_spi *sc = litex_spiflash_spidev_to_sp(spi);
+
+ litex_setbits(sc, SPIFLASH_BB_DIR, 1);
+ return !!((litex_spiflash_spi_rr(sc, SPIFLASH_MISO) & 1));
+}
+
+#define spidelay(nsecs) udelay(nsecs/1000)
+
+#include "spi-bitbang-txrx.h"
+
+static inline void litex_spiflash_spi_chipselect(struct litex_spiflash_spi *sc, int on)
+{
+ litex_setbits(sc, SPIFLASH_BB_CSN, on);
+}
+
+static int litex_spiflash_spi_setup(struct spi_device *spi)
+{
+ struct spi_master *master = spi->master;
+ struct device spi_dev = spi->dev;
+
+ if (spi->bits_per_word != 8) {
+ dev_err(master->dev, "master doesn't support %d bits per word requested by %s\n",
+ spi->bits_per_word, spi_dev.name);
+ return -EINVAL;
+ }
+
+ if ((spi->mode & (SPI_CPHA | SPI_CPOL)) != SPI_MODE_0) {
+ dev_err(master->dev, "master doesn't support SPI_MODE%d requested by %s\n",
+ spi->mode & (SPI_CPHA | SPI_CPOL), spi_dev.name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int litex_spiflash_spi_read(struct spi_device *spi, void *buf, size_t nbyte)
+{
+ ssize_t cnt = 0;
+ u8 *rxf_buf = buf;
+
+ while (cnt < nbyte) {
+ *rxf_buf = bitbang_txrx_be_cpha1(spi, 1000, 1, 0, 8);
+ rxf_buf++;
+ cnt++;
+ }
+
+ return cnt;
+}
+
+static int litex_spiflash_spi_write(struct spi_device *spi,
+ const void *buf, size_t nbyte)
+{
+ ssize_t cnt = 0;
+ const u8 *txf_buf = buf;
+
+ while (cnt < nbyte) {
+ bitbang_txrx_be_cpha1(spi, 1000, 1, (u32)*txf_buf, 8);
+ txf_buf++;
+ cnt++;
+ }
+
+ return 0;
+}
+
+static int litex_spiflash_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
+{
+ struct litex_spiflash_spi *sc = litex_spiflash_spidev_to_sp(spi);
+ struct spi_transfer *t;
+
+ mesg->actual_length = 0;
+
+ /* activate chip select signal */
+ litex_spiflash_spi_chipselect(sc, 0);
+
+ list_for_each_entry(t, &mesg->transfers, transfer_list) {
+
+ if (t->tx_buf)
+ litex_spiflash_spi_write(spi, t->tx_buf, t->len);
+
+ if (t->rx_buf)
+ litex_spiflash_spi_read(spi, t->rx_buf, t->len);
+
+ mesg->actual_length += t->len;
+ }
+
+ /* inactivate chip select signal */
+ litex_spiflash_spi_chipselect(sc, 1);
+
+ return 0;
+}
+
+static void litex_spiflash_spi_enable(struct litex_spiflash_spi *sp)
+{
+ u32 val;
+
+ /* set SPIFLASH_BB_DIR = 0 */
+ val = SPIFLASH_BB_CSN | SPIFLASH_BB_CLK | SPIFLASH_BB_MOSI;
+ litex_spiflash_spi_wr(sp, val, SPIFLASH_BITBANG);
+
+ /* enable GPIO mode */
+ litex_spiflash_spi_wr(sp, 1, SPIFLASH_BITBANG_EN);
+}
+
+static void litex_spiflash_spi_disable(struct litex_spiflash_spi *sp)
+{
+ /* disable GPIO mode */
+ litex_spiflash_spi_wr(sp, 0, SPIFLASH_BITBANG_EN);
+}
+
+static int litex_spiflash_spi_probe(struct device *dev)
+{
+ struct resource *iores;
+ struct spi_master *master;
+ struct litex_spiflash_spi *litex_spiflash_spi;
+
+ litex_spiflash_spi = xzalloc(sizeof(*litex_spiflash_spi));
+ dev->priv = litex_spiflash_spi;
+
+ master = &litex_spiflash_spi->master;
+ master->dev = dev;
+
+ master->bus_num = dev->id;
+ master->setup = litex_spiflash_spi_setup;
+ master->transfer = litex_spiflash_spi_transfer;
+ master->num_chipselect = 1;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ litex_spiflash_spi->regs = IOMEM(iores->start);
+
+ litex_spiflash_spi_enable(litex_spiflash_spi);
+
+ /* inactivate chip select signal */
+ litex_spiflash_spi_chipselect(litex_spiflash_spi, 1);
+
+ spi_register_master(master);
+
+ return 0;
+}
+
+static void litex_spiflash_spi_remove(struct device *dev)
+{
+ struct litex_spiflash_spi *sp = dev->priv;
+
+ litex_spiflash_spi_disable(sp);
+}
+
+static __maybe_unused struct of_device_id litex_spiflash_spi_dt_ids[] = {
+ {
+ .compatible = "litex,spiflash",
+ },
+ {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, litex_spiflash_spi_dt_ids);
+
+static struct driver litex_spiflash_spi_driver = {
+ .name = "litex-spiflash",
+ .probe = litex_spiflash_spi_probe,
+ .remove = litex_spiflash_spi_remove,
+ .of_compatible = DRV_OF_COMPAT(litex_spiflash_spi_dt_ids),
+};
+device_platform_driver(litex_spiflash_spi_driver);
diff --git a/drivers/spi/mvebu_spi.c b/drivers/spi/mvebu_spi.c
index 14ab39603c..e220d1f9ee 100644
--- a/drivers/spi/mvebu_spi.c
+++ b/drivers/spi/mvebu_spi.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Marvell MVEBU SoC SPI controller
* compatible with Dove, Kirkwood, MV78x00, Armada 370/XP
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <common.h>
@@ -356,8 +347,9 @@ static struct of_device_id mvebu_spi_dt_ids[] = {
.data = &mvebu_spi_set_baudrate },
{ }
};
+MODULE_DEVICE_TABLE(of, mvebu_spi_dt_ids);
-static int mvebu_spi_probe(struct device_d *dev)
+static int mvebu_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -365,7 +357,7 @@ static int mvebu_spi_probe(struct device_d *dev)
const struct of_device_id *match;
int ret = 0;
- match = of_match_node(mvebu_spi_dt_ids, dev->device_node);
+ match = of_match_node(mvebu_spi_dt_ids, dev->of_node);
if (!match)
return -EINVAL;
@@ -400,7 +392,7 @@ err_free:
return ret;
}
-static struct driver_d mvebu_spi_driver = {
+static struct driver mvebu_spi_driver = {
.name = "mvebu-spi",
.probe = mvebu_spi_probe,
.of_compatible = DRV_OF_COMPAT(mvebu_spi_dt_ids),
diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c
index 420d122b55..d2ec42f064 100644
--- a/drivers/spi/mxs_spi.c
+++ b/drivers/spi/mxs_spi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Freescale i.MX28 SPI driver
*
@@ -5,12 +6,6 @@
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
*/
#include <common.h>
@@ -23,9 +18,8 @@
#include <stmp-device.h>
#include <linux/clk.h>
#include <linux/err.h>
-#include <mach/generic.h>
-#include <mach/clock.h>
-#include <mach/ssp.h>
+#include <mach/mxs/generic.h>
+#include <mach/mxs/ssp.h>
#define MXS_SPI_MAX_TIMEOUT (10 * MSECOND)
@@ -248,7 +242,7 @@ static int mxs_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
return 0;
}
-static int mxs_spi_probe(struct device_d *dev)
+static int mxs_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -280,7 +274,7 @@ static int mxs_spi_probe(struct device_d *dev)
return 0;
}
-static struct driver_d mxs_spi_driver = {
+static struct driver mxs_spi_driver = {
.name = "mxs_spi",
.probe = mxs_spi_probe,
};
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index beea772aa9..78c3a82338 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2012 Jan Luebbe <j.luebbe@pengutronix.de>
*
@@ -12,20 +13,6 @@
* Copyright (C) 2005, 2006 Nokia Corporation
*
* Modified by Ruslan Araslanov <ruslan.araslanov@vitecmm.com>
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <common.h>
@@ -345,18 +332,19 @@ static int omap3_spi_setup(struct spi_device *spi)
return 0;
}
-static int omap3_spi_probe_dt(struct device_d *dev, struct omap3_spi_master *omap3_master)
+static int omap3_spi_probe_dt(struct device *dev,
+ struct omap3_spi_master *omap3_master)
{
- if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node)
+ if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->of_node)
return -ENODEV;
- if (of_property_read_bool(dev->device_node, "ti,pindir-d0-out-d1-in"))
+ if (of_property_read_bool(dev->of_node, "ti,pindir-d0-out-d1-in"))
omap3_master->swap_miso_mosi = 1;
return 0;
}
-static int omap3_spi_probe(struct device_d *dev)
+static int omap3_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -434,6 +422,7 @@ static __maybe_unused struct of_device_id omap_spi_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, omap_spi_dt_ids);
static struct platform_device_id omap_spi_ids[] = {
{
@@ -447,7 +436,7 @@ static struct platform_device_id omap_spi_ids[] = {
},
};
-static struct driver_d omap3_spi_driver = {
+static struct driver omap3_spi_driver = {
.name = "omap-spi",
.probe = omap3_spi_probe,
.of_compatible = DRV_OF_COMPAT(omap_spi_dt_ids),
diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h
index 24cdc5301c..b131b24bf2 100644
--- a/drivers/spi/omap3_spi.h
+++ b/drivers/spi/omap3_spi.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Register definitions for the OMAP3 McSPI Controller
*
@@ -7,20 +8,6 @@
* Copyright (C) 2005, 2006 Nokia Corporation
*
* Modified by Ruslan Araslanov <ruslan.araslanov@vitecmm.com>
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#ifndef _OMAP3_SPI_H_
diff --git a/drivers/spi/spi-bitbang-txrx.h b/drivers/spi/spi-bitbang-txrx.h
index 078ab8c6c0..7b75f2f862 100644
--- a/drivers/spi/spi-bitbang-txrx.h
+++ b/drivers/spi/spi-bitbang-txrx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Mix this utility code with some glue code to get one of several types of
* simple SPI master driver. Two do polled word-at-a-time I/O:
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
new file mode 100644
index 0000000000..f032e2673e
--- /dev/null
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -0,0 +1,656 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Copyright 2013 Freescale Semiconductor, Inc.
+//
+// Freescale DSPI driver
+// This file contains a driver for the Freescale DSPI
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <init.h>
+#include <io.h>
+#include <linux/regmap.h>
+#include <spi/spi.h>
+#include <linux/clk.h>
+#include <linux/math64.h>
+
+#define DRIVER_NAME "fsl-dspi"
+
+#define DSPI_FIFO_SIZE 4
+#define DSPI_DMA_BUFSIZE (DSPI_FIFO_SIZE * 1024)
+
+#define SPI_MCR 0x00
+#define SPI_MCR_MASTER BIT(31)
+#define SPI_MCR_PCSIS (0x3F << 16)
+#define SPI_MCR_CLR_TXF BIT(11)
+#define SPI_MCR_CLR_RXF BIT(10)
+#define SPI_MCR_XSPI BIT(3)
+
+#define SPI_TCR 0x08
+#define SPI_TCR_GET_TCNT(x) (((x) & GENMASK(31, 16)) >> 16)
+
+#define SPI_CTAR(x) (0x0c + (((x) & GENMASK(1, 0)) * 4))
+#define SPI_CTAR_FMSZ(x) (((x) << 27) & GENMASK(30, 27))
+#define SPI_CTAR_CPOL BIT(26)
+#define SPI_CTAR_CPHA BIT(25)
+#define SPI_CTAR_LSBFE BIT(24)
+#define SPI_CTAR_PCSSCK(x) (((x) << 22) & GENMASK(23, 22))
+#define SPI_CTAR_PASC(x) (((x) << 20) & GENMASK(21, 20))
+#define SPI_CTAR_PDT(x) (((x) << 18) & GENMASK(19, 18))
+#define SPI_CTAR_PBR(x) (((x) << 16) & GENMASK(17, 16))
+#define SPI_CTAR_CSSCK(x) (((x) << 12) & GENMASK(15, 12))
+#define SPI_CTAR_ASC(x) (((x) << 8) & GENMASK(11, 8))
+#define SPI_CTAR_DT(x) (((x) << 4) & GENMASK(7, 4))
+#define SPI_CTAR_BR(x) ((x) & GENMASK(3, 0))
+#define SPI_CTAR_SCALE_BITS 0xf
+
+#define SPI_CTAR0_SLAVE 0x0c
+
+#define SPI_SR 0x2c
+#define SPI_SR_TCFQF BIT(31)
+#define SPI_SR_EOQF BIT(28)
+#define SPI_SR_TFUF BIT(27)
+#define SPI_SR_TFFF BIT(25)
+#define SPI_SR_CMDTCF BIT(23)
+#define SPI_SR_SPEF BIT(21)
+#define SPI_SR_RFOF BIT(19)
+#define SPI_SR_TFIWF BIT(18)
+#define SPI_SR_RFDF BIT(17)
+#define SPI_SR_CMDFFF BIT(16)
+#define SPI_SR_CLEAR (SPI_SR_TCFQF | SPI_SR_EOQF | \
+ SPI_SR_TFUF | SPI_SR_TFFF | \
+ SPI_SR_CMDTCF | SPI_SR_SPEF | \
+ SPI_SR_RFOF | SPI_SR_TFIWF | \
+ SPI_SR_RFDF | SPI_SR_CMDFFF)
+
+#define SPI_RSER_TFFFE BIT(25)
+#define SPI_RSER_TFFFD BIT(24)
+#define SPI_RSER_RFDFE BIT(17)
+#define SPI_RSER_RFDFD BIT(16)
+
+#define SPI_RSER 0x30
+#define SPI_RSER_TCFQE BIT(31)
+#define SPI_RSER_EOQFE BIT(28)
+
+#define SPI_PUSHR 0x34
+#define SPI_PUSHR_CMD_CONT BIT(15)
+#define SPI_PUSHR_CMD_CTAS(x) (((x) << 12 & GENMASK(14, 12)))
+#define SPI_PUSHR_CMD_EOQ BIT(11)
+#define SPI_PUSHR_CMD_CTCNT BIT(10)
+#define SPI_PUSHR_CMD_PCS(x) (BIT(x) & GENMASK(5, 0))
+
+#define SPI_PUSHR_SLAVE 0x34
+
+#define SPI_POPR 0x38
+
+#define SPI_TXFR0 0x3c
+#define SPI_TXFR1 0x40
+#define SPI_TXFR2 0x44
+#define SPI_TXFR3 0x48
+#define SPI_RXFR0 0x7c
+#define SPI_RXFR1 0x80
+#define SPI_RXFR2 0x84
+#define SPI_RXFR3 0x88
+
+#define SPI_CTARE(x) (0x11c + (((x) & GENMASK(1, 0)) * 4))
+#define SPI_CTARE_FMSZE(x) (((x) & 0x1) << 16)
+#define SPI_CTARE_DTCP(x) ((x) & 0x7ff)
+
+#define SPI_SREX 0x13c
+
+#define SPI_FRAME_BITS(bits) SPI_CTAR_FMSZ((bits) - 1)
+#define SPI_FRAME_EBITS(bits) SPI_CTARE_FMSZE(((bits) - 1) >> 4)
+
+/* Register offsets for regmap_pushr */
+#define PUSHR_CMD 0x0
+#define PUSHR_TX 0x2
+
+#define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000)
+
+struct chip_data {
+ u32 ctar_val;
+ u16 void_write_data;
+};
+
+struct fsl_dspi_devtype_data {
+ u8 max_clock_factor;
+ bool ptp_sts_supported;
+ bool xspi_mode;
+};
+
+static const struct fsl_dspi_devtype_data ls1021a_v1_data = {
+ .max_clock_factor = 8,
+ .ptp_sts_supported = true,
+ .xspi_mode = true,
+};
+
+static const struct fsl_dspi_devtype_data ls2085a_data = {
+ .max_clock_factor = 8,
+ .ptp_sts_supported = true,
+};
+
+struct fsl_dspi {
+ struct spi_controller ctlr;
+ struct device *dev;
+
+ struct regmap *regmap;
+ struct regmap *regmap_pushr;
+ int irq;
+ struct clk *clk;
+
+ struct spi_transfer *cur_transfer;
+ struct spi_message *cur_msg;
+ struct chip_data *cur_chip;
+ size_t progress;
+ size_t len;
+ const void *tx;
+ void *rx;
+ void *rx_end;
+ u16 void_write_data;
+ u16 tx_cmd;
+ u8 bits_per_word;
+ u8 bytes_per_word;
+ const struct fsl_dspi_devtype_data *devtype_data;
+};
+
+static u32 dspi_pop_tx(struct fsl_dspi *dspi)
+{
+ u32 txdata = 0;
+
+ if (dspi->tx) {
+ if (dspi->bytes_per_word == 1)
+ txdata = *(u8 *)dspi->tx;
+ else if (dspi->bytes_per_word == 2)
+ txdata = *(u16 *)dspi->tx;
+ else /* dspi->bytes_per_word == 4 */
+ txdata = *(u32 *)dspi->tx;
+ dspi->tx += dspi->bytes_per_word;
+ }
+ dspi->len -= dspi->bytes_per_word;
+ return txdata;
+}
+
+static u32 dspi_pop_tx_pushr(struct fsl_dspi *dspi)
+{
+ u16 cmd = dspi->tx_cmd, data = dspi_pop_tx(dspi);
+
+ if (dspi->len > 0)
+ cmd |= SPI_PUSHR_CMD_CONT;
+ return cmd << 16 | data;
+}
+
+static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata)
+{
+ if (!dspi->rx)
+ return;
+
+ /* Mask off undefined bits */
+ rxdata &= (1 << dspi->bits_per_word) - 1;
+
+ if (dspi->bytes_per_word == 1)
+ *(u8 *)dspi->rx = rxdata;
+ else if (dspi->bytes_per_word == 2)
+ *(u16 *)dspi->rx = rxdata;
+ else /* dspi->bytes_per_word == 4 */
+ *(u32 *)dspi->rx = rxdata;
+ dspi->rx += dspi->bytes_per_word;
+}
+
+static void hz_to_spi_baud(char *pbr, char *br, int speed_hz,
+ unsigned long clkrate)
+{
+ /* Valid baud rate pre-scaler values */
+ int pbr_tbl[4] = {2, 3, 5, 7};
+ int brs[16] = { 2, 4, 6, 8,
+ 16, 32, 64, 128,
+ 256, 512, 1024, 2048,
+ 4096, 8192, 16384, 32768 };
+ int scale_needed, scale, minscale = INT_MAX;
+ int i, j;
+
+ scale_needed = clkrate / speed_hz;
+ if (clkrate % speed_hz)
+ scale_needed++;
+
+ for (i = 0; i < ARRAY_SIZE(brs); i++)
+ for (j = 0; j < ARRAY_SIZE(pbr_tbl); j++) {
+ scale = brs[i] * pbr_tbl[j];
+ if (scale >= scale_needed) {
+ if (scale < minscale) {
+ minscale = scale;
+ *br = i;
+ *pbr = j;
+ }
+ break;
+ }
+ }
+
+ if (minscale == INT_MAX) {
+ pr_warn("Can not find valid baud rate,speed_hz is %d,clkrate is %ld, we use the max prescaler value.\n",
+ speed_hz, clkrate);
+ *pbr = ARRAY_SIZE(pbr_tbl) - 1;
+ *br = ARRAY_SIZE(brs) - 1;
+ }
+}
+
+static void ns_delay_scale(char *psc, char *sc, int delay_ns,
+ unsigned long clkrate)
+{
+ int scale_needed, scale, minscale = INT_MAX;
+ int pscale_tbl[4] = {1, 3, 5, 7};
+ u32 remainder;
+ int i, j;
+
+ scale_needed = div_u64_rem((u64)delay_ns * clkrate, NSEC_PER_SEC,
+ &remainder);
+ if (remainder)
+ scale_needed++;
+
+ for (i = 0; i < ARRAY_SIZE(pscale_tbl); i++)
+ for (j = 0; j <= SPI_CTAR_SCALE_BITS; j++) {
+ scale = pscale_tbl[i] * (2 << j);
+ if (scale >= scale_needed) {
+ if (scale < minscale) {
+ minscale = scale;
+ *psc = i;
+ *sc = j;
+ }
+ break;
+ }
+ }
+
+ if (minscale == INT_MAX) {
+ pr_warn("Cannot find correct scale values for %dns delay at clkrate %ld, using max prescaler value",
+ delay_ns, clkrate);
+ *psc = ARRAY_SIZE(pscale_tbl) - 1;
+ *sc = SPI_CTAR_SCALE_BITS;
+ }
+}
+
+static void fifo_write(struct fsl_dspi *dspi)
+{
+ regmap_write(dspi->regmap, SPI_PUSHR, dspi_pop_tx_pushr(dspi));
+}
+
+static void cmd_fifo_write(struct fsl_dspi *dspi)
+{
+ u16 cmd = dspi->tx_cmd;
+
+ if (dspi->len > 0)
+ cmd |= SPI_PUSHR_CMD_CONT;
+ regmap_write(dspi->regmap_pushr, PUSHR_CMD, cmd);
+}
+
+static void tx_fifo_write(struct fsl_dspi *dspi, u16 txdata)
+{
+ regmap_write(dspi->regmap_pushr, PUSHR_TX, txdata);
+}
+
+static void dspi_tcfq_write(struct fsl_dspi *dspi)
+{
+ /* Clear transfer count */
+ dspi->tx_cmd |= SPI_PUSHR_CMD_CTCNT;
+
+ if (dspi->devtype_data->xspi_mode && dspi->bits_per_word > 16) {
+ /* Write the CMD FIFO entry first, and then the two
+ * corresponding TX FIFO entries.
+ */
+ u32 data = dspi_pop_tx(dspi);
+
+ cmd_fifo_write(dspi);
+ tx_fifo_write(dspi, data & 0xFFFF);
+ tx_fifo_write(dspi, data >> 16);
+ } else {
+ /* Write one entry to both TX FIFO and CMD FIFO
+ * simultaneously.
+ */
+ fifo_write(dspi);
+ }
+}
+
+static u32 fifo_read(struct fsl_dspi *dspi)
+{
+ u32 rxdata = 0;
+
+ regmap_read(dspi->regmap, SPI_POPR, &rxdata);
+ return rxdata;
+}
+
+static void dspi_tcfq_read(struct fsl_dspi *dspi)
+{
+ dspi_push_rx(dspi, fifo_read(dspi));
+}
+
+static int dspi_rxtx(struct fsl_dspi *dspi)
+{
+ struct spi_message *msg = dspi->cur_msg;
+ u16 spi_tcnt;
+ u32 spi_tcr;
+
+ /* Get transfer counter (in number of SPI transfers). It was
+ * reset to 0 when transfer(s) were started.
+ */
+ regmap_read(dspi->regmap, SPI_TCR, &spi_tcr);
+ spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr);
+ /* Update total number of bytes that were transferred */
+ msg->actual_length += spi_tcnt * dspi->bytes_per_word;
+ dspi->progress += spi_tcnt;
+
+ dspi_tcfq_read(dspi);
+ if (!dspi->len)
+ /* Success! */
+ return 0;
+
+ dspi_tcfq_write(dspi);
+
+ return -EINPROGRESS;
+}
+
+static int dspi_poll(struct fsl_dspi *dspi)
+{
+ int tries = 1000;
+ u32 spi_sr;
+
+ do {
+ regmap_read(dspi->regmap, SPI_SR, &spi_sr);
+ regmap_write(dspi->regmap, SPI_SR, spi_sr);
+
+ if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF))
+ break;
+ udelay(1);
+ } while (--tries);
+
+ if (!tries)
+ return -ETIMEDOUT;
+
+ return dspi_rxtx(dspi);
+}
+
+static int dspi_transfer_one_message(struct spi_device *spi,
+ struct spi_message *message)
+{
+ struct fsl_dspi *dspi = container_of(spi->master, struct fsl_dspi, ctlr);
+ struct spi_transfer *transfer;
+ int status = 0;
+
+ message->actual_length = 0;
+
+ list_for_each_entry(transfer, &message->transfers, transfer_list) {
+ dspi->cur_transfer = transfer;
+ dspi->cur_msg = message;
+ dspi->cur_chip = spi->controller_data;
+ /* Prepare command word for CMD FIFO */
+ dspi->tx_cmd = SPI_PUSHR_CMD_CTAS(0) |
+ SPI_PUSHR_CMD_PCS(spi->chip_select);
+ if (list_is_last(&dspi->cur_transfer->transfer_list,
+ &dspi->cur_msg->transfers)) {
+ /* Leave PCS activated after last transfer when
+ * cs_change is set.
+ */
+ if (transfer->cs_change)
+ dspi->tx_cmd |= SPI_PUSHR_CMD_CONT;
+ } else {
+ /* Keep PCS active between transfers in same message
+ * when cs_change is not set, and de-activate PCS
+ * between transfers in the same message when
+ * cs_change is set.
+ */
+ if (!transfer->cs_change)
+ dspi->tx_cmd |= SPI_PUSHR_CMD_CONT;
+ }
+
+ dspi->void_write_data = dspi->cur_chip->void_write_data;
+
+ dspi->tx = transfer->tx_buf;
+ dspi->rx = transfer->rx_buf;
+ dspi->rx_end = dspi->rx + transfer->len;
+ dspi->len = transfer->len;
+ dspi->progress = 0;
+ /* Validated transfer specific frame size (defaults applied) */
+ dspi->bits_per_word = transfer->bits_per_word;
+
+ if (transfer->bits_per_word <= 8)
+ dspi->bytes_per_word = 1;
+ else if (transfer->bits_per_word <= 16)
+ dspi->bytes_per_word = 2;
+ else
+ dspi->bytes_per_word = 4;
+
+ regmap_update_bits(dspi->regmap, SPI_MCR,
+ SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF,
+ SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF);
+ regmap_write(dspi->regmap, SPI_CTAR(0),
+ dspi->cur_chip->ctar_val |
+ SPI_FRAME_BITS(transfer->bits_per_word));
+ if (dspi->devtype_data->xspi_mode)
+ regmap_write(dspi->regmap, SPI_CTARE(0),
+ SPI_FRAME_EBITS(transfer->bits_per_word) |
+ SPI_CTARE_DTCP(1));
+
+ regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE);
+ dspi_tcfq_write(dspi);
+
+ do {
+ status = dspi_poll(dspi);
+ } while (status == -EINPROGRESS);
+
+ if (status)
+ dev_err(dspi->dev,
+ "Waiting for transfer to complete failed!\n");
+ }
+
+ message->status = status;
+
+ return status;
+}
+
+static int dspi_setup(struct spi_device *spi)
+{
+ struct fsl_dspi *dspi = container_of(spi->master, struct fsl_dspi, ctlr);
+ unsigned char br = 0, pbr = 0, pcssck = 0, cssck = 0;
+ u32 cs_sck_delay = 0, sck_cs_delay = 0;
+ unsigned char pasc = 0, asc = 0;
+ struct chip_data *chip;
+ unsigned long clkrate;
+
+ /* Only alloc on first setup */
+ chip = spi->controller_data;
+ if (chip == NULL) {
+ chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ }
+
+ of_property_read_u32(spi->dev.of_node, "fsl,spi-cs-sck-delay",
+ &cs_sck_delay);
+
+ of_property_read_u32(spi->dev.of_node, "fsl,spi-sck-cs-delay",
+ &sck_cs_delay);
+
+ chip->void_write_data = 0;
+
+ clkrate = clk_get_rate(dspi->clk);
+ hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate);
+
+ /* Set PCS to SCK delay scale values */
+ ns_delay_scale(&pcssck, &cssck, cs_sck_delay, clkrate);
+
+ /* Set After SCK delay scale values */
+ ns_delay_scale(&pasc, &asc, sck_cs_delay, clkrate);
+
+ chip->ctar_val = 0;
+ if (spi->mode & SPI_CPOL)
+ chip->ctar_val |= SPI_CTAR_CPOL;
+ if (spi->mode & SPI_CPHA)
+ chip->ctar_val |= SPI_CTAR_CPHA;
+
+ chip->ctar_val |= SPI_CTAR_PCSSCK(pcssck) |
+ SPI_CTAR_CSSCK(cssck) |
+ SPI_CTAR_PASC(pasc) |
+ SPI_CTAR_ASC(asc) |
+ SPI_CTAR_PBR(pbr) |
+ SPI_CTAR_BR(br);
+
+ if (spi->mode & SPI_LSB_FIRST)
+ chip->ctar_val |= SPI_CTAR_LSBFE;
+
+ spi->controller_data = chip;
+
+ return 0;
+}
+
+static void dspi_cleanup(struct spi_device *spi)
+{
+ struct chip_data *chip = spi->controller_data;
+
+ dev_dbg(&spi->dev, "spi_device %u.%u cleanup\n",
+ spi->controller->bus_num, spi->chip_select);
+
+ kfree(chip);
+}
+
+static const struct of_device_id fsl_dspi_dt_ids[] = {
+ { .compatible = "fsl,ls1021a-v1.0-dspi", .data = &ls1021a_v1_data, },
+ { .compatible = "fsl,ls2085a-dspi", .data = &ls2085a_data, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids);
+
+static const struct regmap_config dspi_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x88,
+};
+
+static const struct regmap_config dspi_xspi_regmap_config[] = {
+ {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x13c,
+ }, {
+ .name = "pushr",
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_stride = 2,
+ .max_register = 0x2,
+ },
+};
+
+static void dspi_init(struct fsl_dspi *dspi)
+{
+ unsigned int mcr = SPI_MCR_PCSIS;
+
+ if (dspi->devtype_data->xspi_mode)
+ mcr |= SPI_MCR_XSPI;
+ mcr |= SPI_MCR_MASTER;
+
+ regmap_write(dspi->regmap, SPI_MCR, mcr);
+ regmap_write(dspi->regmap, SPI_SR, SPI_SR_CLEAR);
+ if (dspi->devtype_data->xspi_mode)
+ regmap_write(dspi->regmap, SPI_CTARE(0),
+ SPI_CTARE_FMSZE(0) | SPI_CTARE_DTCP(1));
+}
+
+static int dspi_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ const struct regmap_config *regmap_config;
+ struct spi_master *master;
+ int ret, cs_num, bus_num = -1;
+ struct fsl_dspi *dspi;
+ struct resource *res;
+ void __iomem *base;
+
+ dspi = xzalloc(sizeof(*dspi));
+
+ dspi->dev = dev;
+ master = &dspi->ctlr;
+
+ master->dev = dev;
+ master->setup = dspi_setup;
+ master->transfer = dspi_transfer_one_message;
+
+ master->cleanup = dspi_cleanup;
+
+ ret = of_property_read_u32(np, "spi-num-chipselects", &cs_num);
+ if (ret < 0) {
+ dev_err(dev, "can't get spi-num-chipselects\n");
+ goto out_ctlr_put;
+ }
+ master->num_chipselect = cs_num;
+
+ of_property_read_u32(np, "bus-num", &bus_num);
+ master->bus_num = bus_num;
+
+ ret = dev_get_drvdata(dev, (const void **)&dspi->devtype_data);
+ if (ret)
+ return -ENODEV;
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ base = IOMEM(res->start);
+
+ if (dspi->devtype_data->xspi_mode)
+ regmap_config = &dspi_xspi_regmap_config[0];
+ else
+ regmap_config = &dspi_regmap_config;
+
+ dspi->regmap = regmap_init_mmio(dev, base, regmap_config);
+ if (IS_ERR(dspi->regmap)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(dspi->regmap));
+ ret = PTR_ERR(dspi->regmap);
+ goto out_ctlr_put;
+ }
+
+ if (dspi->devtype_data->xspi_mode) {
+ dspi->regmap_pushr = regmap_init_mmio(
+ dev, base + SPI_PUSHR,
+ &dspi_xspi_regmap_config[1]);
+ if (IS_ERR(dspi->regmap_pushr)) {
+ dev_err(dev,
+ "failed to init pushr regmap: %ld\n",
+ PTR_ERR(dspi->regmap_pushr));
+ ret = PTR_ERR(dspi->regmap_pushr);
+ goto out_ctlr_put;
+ }
+ }
+
+ dspi->clk = clk_get(dev, "dspi");
+ if (IS_ERR(dspi->clk)) {
+ ret = PTR_ERR(dspi->clk);
+ dev_err(dev, "unable to get clock\n");
+ goto out_ctlr_put;
+ }
+ ret = clk_enable(dspi->clk);
+ if (ret)
+ goto out_ctlr_put;
+
+ dspi_init(dspi);
+
+ ret = spi_register_master(master);
+ if (ret != 0) {
+ dev_err(dev, "Problem registering DSPI ctlr\n");
+ goto out_clk_put;
+ }
+
+ return ret;
+
+out_clk_put:
+ clk_disable(dspi->clk);
+out_ctlr_put:
+
+ return ret;
+}
+
+static struct driver fsl_dspi_driver = {
+ .name = "fsl-dspi",
+ .probe = dspi_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_dspi_dt_ids),
+};
+coredevice_platform_driver(fsl_dspi_driver);
diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c
index e22c3099fe..17e6d1df86 100644
--- a/drivers/spi/spi-fsl-qspi.c
+++ b/drivers/spi/spi-fsl-qspi.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Freescale QuadSPI driver.
@@ -239,7 +239,7 @@ struct fsl_qspi {
void __iomem *ahb_addr;
u32 memmap_phy;
struct clk *clk, *clk_en;
- struct device_d *dev;
+ struct device *dev;
struct spi_controller ctlr;
const struct fsl_qspi_devtype_data *devtype_data;
struct mutex lock;
@@ -743,7 +743,7 @@ static int fsl_qspi_setup(struct spi_device *spi)
static const char *fsl_qspi_get_name(struct spi_mem *mem)
{
struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
- struct device_d *dev = &mem->spi->dev;
+ struct device *dev = &mem->spi->dev;
const char *name;
/*
@@ -751,7 +751,7 @@ static const char *fsl_qspi_get_name(struct spi_mem *mem)
* mtd/spi-nor/fsl-quadspi.c, we set a custom name derived from the
* platform_device of the controller.
*/
- if (of_get_available_child_count(q->dev->device_node) == 1)
+ if (of_get_available_child_count(q->dev->of_node) == 1)
return dev_name(q->dev);
name = basprintf("%s-%d", dev_name(q->dev), mem->spi->chip_select);
@@ -770,7 +770,7 @@ static const struct spi_controller_mem_ops fsl_qspi_mem_ops = {
.get_name = fsl_qspi_get_name,
};
-static int fsl_qspi_probe(struct device_d *dev)
+static int fsl_qspi_probe(struct device *dev)
{
struct spi_controller *ctlr;
struct resource *res;
@@ -860,8 +860,9 @@ static const struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,ls2080a-qspi", .data = &ls2080a_data, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
-static struct driver_d fsl_qspi_driver = {
+static struct driver fsl_qspi_driver = {
.name = "fsl-quadspi",
.probe = fsl_qspi_probe,
.of_compatible = DRV_OF_COMPAT(fsl_qspi_dt_ids),
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index b438ed3dcc..1e4120a2dc 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 Exceet Electronics GmbH
* Copyright (C) 2018 Bootlin
diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
new file mode 100644
index 0000000000..d8675779da
--- /dev/null
+++ b/drivers/spi/spi-nxp-fspi.c
@@ -0,0 +1,1064 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * NXP FlexSPI(FSPI) controller driver.
+ *
+ * Copyright 2019-2020 NXP
+ * Copyright 2020 Puresoftware Ltd.
+ * Copyright 2021 Westermo Network Technologies AB
+ *
+ * FlexSPI is a flexsible SPI host controller which supports two SPI
+ * channels and up to 4 external devices. Each channel supports
+ * Single/Dual/Quad/Octal mode data transfer (1/2/4/8 bidirectional
+ * data lines).
+ *
+ * FlexSPI controller is driven by the LUT(Look-up Table) registers
+ * LUT registers are a look-up-table for sequences of instructions.
+ * A valid sequence consists of four LUT registers.
+ * Maximum 32 LUT sequences can be programmed simultaneously.
+ *
+ * LUTs are being created at run-time based on the commands passed
+ * from the spi-mem framework, thus using single LUT index.
+ *
+ * Software triggered Flash read/write access by IP Bus.
+ *
+ * Memory mapped read access by AHB Bus.
+ *
+ * Based on SPI MEM interface and spi-fsl-qspi.c driver.
+ *
+ * Author:
+ * Yogesh Narayan Gaur <yogeshnarayan.gaur@nxp.com>
+ * Boris Brezillon <bbrezillon@kernel.org>
+ * Frieder Schrempf <frieder.schrempf@kontron.de>
+ * Joacim Zetterling <joacim.zetterling@westermo.com>
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <init.h>
+#include <io.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/mutex.h>
+#include <linux/sizes.h>
+#include <of.h>
+#include <of_device.h>
+#include <stdbool.h>
+
+#include <spi/spi.h>
+#include <linux/spi/spi-mem.h>
+
+/*
+ * The driver only uses one single LUT entry, that is updated on
+ * each call of exec_op(). Index 0 is preset at boot with a basic
+ * read operation, so let's use the last entry (31).
+ */
+#define SEQID_LUT 31
+
+/* Registers used by the driver */
+#define FSPI_MCR0 0x00
+#define FSPI_MCR0_AHB_TIMEOUT(x) ((x) << 24)
+#define FSPI_MCR0_IP_TIMEOUT(x) ((x) << 16)
+#define FSPI_MCR0_LEARN_EN BIT(15)
+#define FSPI_MCR0_SCRFRUN_EN BIT(14)
+#define FSPI_MCR0_OCTCOMB_EN BIT(13)
+#define FSPI_MCR0_DOZE_EN BIT(12)
+#define FSPI_MCR0_HSEN BIT(11)
+#define FSPI_MCR0_SERCLKDIV BIT(8)
+#define FSPI_MCR0_ATDF_EN BIT(7)
+#define FSPI_MCR0_ARDF_EN BIT(6)
+#define FSPI_MCR0_RXCLKSRC(x) ((x) << 4)
+#define FSPI_MCR0_END_CFG(x) ((x) << 2)
+#define FSPI_MCR0_MDIS BIT(1)
+#define FSPI_MCR0_SWRST BIT(0)
+
+#define FSPI_MCR1 0x04
+#define FSPI_MCR1_SEQ_TIMEOUT(x) ((x) << 16)
+#define FSPI_MCR1_AHB_TIMEOUT(x) (x)
+
+#define FSPI_MCR2 0x08
+#define FSPI_MCR2_IDLE_WAIT(x) ((x) << 24)
+#define FSPI_MCR2_SAMEDEVICEEN BIT(15)
+#define FSPI_MCR2_CLRLRPHS BIT(14)
+#define FSPI_MCR2_ABRDATSZ BIT(8)
+#define FSPI_MCR2_ABRLEARN BIT(7)
+#define FSPI_MCR2_ABR_READ BIT(6)
+#define FSPI_MCR2_ABRWRITE BIT(5)
+#define FSPI_MCR2_ABRDUMMY BIT(4)
+#define FSPI_MCR2_ABR_MODE BIT(3)
+#define FSPI_MCR2_ABRCADDR BIT(2)
+#define FSPI_MCR2_ABRRADDR BIT(1)
+#define FSPI_MCR2_ABR_CMD BIT(0)
+
+#define FSPI_AHBCR 0x0c
+#define FSPI_AHBCR_RDADDROPT BIT(6)
+#define FSPI_AHBCR_PREF_EN BIT(5)
+#define FSPI_AHBCR_BUFF_EN BIT(4)
+#define FSPI_AHBCR_CACH_EN BIT(3)
+#define FSPI_AHBCR_CLRTXBUF BIT(2)
+#define FSPI_AHBCR_CLRRXBUF BIT(1)
+#define FSPI_AHBCR_PAR_EN BIT(0)
+
+#define FSPI_INTEN 0x10
+#define FSPI_INTEN_SCLKSBWR BIT(9)
+#define FSPI_INTEN_SCLKSBRD BIT(8)
+#define FSPI_INTEN_DATALRNFL BIT(7)
+#define FSPI_INTEN_IPTXWE BIT(6)
+#define FSPI_INTEN_IPRXWA BIT(5)
+#define FSPI_INTEN_AHBCMDERR BIT(4)
+#define FSPI_INTEN_IPCMDERR BIT(3)
+#define FSPI_INTEN_AHBCMDGE BIT(2)
+#define FSPI_INTEN_IPCMDGE BIT(1)
+#define FSPI_INTEN_IPCMDDONE BIT(0)
+
+#define FSPI_INTR 0x14
+#define FSPI_INTR_SCLKSBWR BIT(9)
+#define FSPI_INTR_SCLKSBRD BIT(8)
+#define FSPI_INTR_DATALRNFL BIT(7)
+#define FSPI_INTR_IPTXWE BIT(6)
+#define FSPI_INTR_IPRXWA BIT(5)
+#define FSPI_INTR_AHBCMDERR BIT(4)
+#define FSPI_INTR_IPCMDERR BIT(3)
+#define FSPI_INTR_AHBCMDGE BIT(2)
+#define FSPI_INTR_IPCMDGE BIT(1)
+#define FSPI_INTR_IPCMDDONE BIT(0)
+
+#define FSPI_LUTKEY 0x18
+#define FSPI_LUTKEY_VALUE 0x5AF05AF0
+
+#define FSPI_LCKCR 0x1C
+
+#define FSPI_LCKER_LOCK 0x1
+#define FSPI_LCKER_UNLOCK 0x2
+
+#define FSPI_BUFXCR_INVALID_MSTRID 0xE
+#define FSPI_AHBRX_BUF0CR0 0x20
+#define FSPI_AHBRX_BUF1CR0 0x24
+#define FSPI_AHBRX_BUF2CR0 0x28
+#define FSPI_AHBRX_BUF3CR0 0x2C
+#define FSPI_AHBRX_BUF4CR0 0x30
+#define FSPI_AHBRX_BUF5CR0 0x34
+#define FSPI_AHBRX_BUF6CR0 0x38
+#define FSPI_AHBRX_BUF7CR0 0x3C
+#define FSPI_AHBRXBUF0CR7_PREF BIT(31)
+
+#define FSPI_AHBRX_BUF0CR1 0x40
+#define FSPI_AHBRX_BUF1CR1 0x44
+#define FSPI_AHBRX_BUF2CR1 0x48
+#define FSPI_AHBRX_BUF3CR1 0x4C
+#define FSPI_AHBRX_BUF4CR1 0x50
+#define FSPI_AHBRX_BUF5CR1 0x54
+#define FSPI_AHBRX_BUF6CR1 0x58
+#define FSPI_AHBRX_BUF7CR1 0x5C
+
+#define FSPI_FLSHA1CR0 0x60
+#define FSPI_FLSHA2CR0 0x64
+#define FSPI_FLSHB1CR0 0x68
+#define FSPI_FLSHB2CR0 0x6C
+#define FSPI_FLSHXCR0_SZ_KB 10
+#define FSPI_FLSHXCR0_SZ(x) ((x) >> FSPI_FLSHXCR0_SZ_KB)
+
+#define FSPI_FLSHA1CR1 0x70
+#define FSPI_FLSHA2CR1 0x74
+#define FSPI_FLSHB1CR1 0x78
+#define FSPI_FLSHB2CR1 0x7C
+#define FSPI_FLSHXCR1_CSINTR(x) ((x) << 16)
+#define FSPI_FLSHXCR1_CAS(x) ((x) << 11)
+#define FSPI_FLSHXCR1_WA BIT(10)
+#define FSPI_FLSHXCR1_TCSH(x) ((x) << 5)
+#define FSPI_FLSHXCR1_TCSS(x) (x)
+
+#define FSPI_FLSHA1CR2 0x80
+#define FSPI_FLSHA2CR2 0x84
+#define FSPI_FLSHB1CR2 0x88
+#define FSPI_FLSHB2CR2 0x8C
+#define FSPI_FLSHXCR2_CLRINSP BIT(24)
+#define FSPI_FLSHXCR2_AWRWAIT BIT(16)
+#define FSPI_FLSHXCR2_AWRSEQN_SHIFT 13
+#define FSPI_FLSHXCR2_AWRSEQI_SHIFT 8
+#define FSPI_FLSHXCR2_ARDSEQN_SHIFT 5
+#define FSPI_FLSHXCR2_ARDSEQI_SHIFT 0
+
+#define FSPI_IPCR0 0xA0
+
+#define FSPI_IPCR1 0xA4
+#define FSPI_IPCR1_IPAREN BIT(31)
+#define FSPI_IPCR1_SEQNUM_SHIFT 24
+#define FSPI_IPCR1_SEQID_SHIFT 16
+#define FSPI_IPCR1_IDATSZ(x) (x)
+
+#define FSPI_IPCMD 0xB0
+#define FSPI_IPCMD_TRG BIT(0)
+
+#define FSPI_DLPR 0xB4
+
+#define FSPI_IPRXFCR 0xB8
+#define FSPI_IPRXFCR_CLR BIT(0)
+#define FSPI_IPRXFCR_DMA_EN BIT(1)
+#define FSPI_IPRXFCR_WMRK(x) ((x) << 2)
+
+#define FSPI_IPTXFCR 0xBC
+#define FSPI_IPTXFCR_CLR BIT(0)
+#define FSPI_IPTXFCR_DMA_EN BIT(1)
+#define FSPI_IPTXFCR_WMRK(x) ((x) << 2)
+
+#define FSPI_DLLACR 0xC0
+#define FSPI_DLLACR_OVRDEN BIT(8)
+
+#define FSPI_DLLBCR 0xC4
+#define FSPI_DLLBCR_OVRDEN BIT(8)
+
+#define FSPI_STS0 0xE0
+#define FSPI_STS0_DLPHB(x) ((x) << 8)
+#define FSPI_STS0_DLPHA(x) ((x) << 4)
+#define FSPI_STS0_CMD_SRC(x) ((x) << 2)
+#define FSPI_STS0_ARB_IDLE BIT(1)
+#define FSPI_STS0_SEQ_IDLE BIT(0)
+
+#define FSPI_STS1 0xE4
+#define FSPI_STS1_IP_ERRCD(x) ((x) << 24)
+#define FSPI_STS1_IP_ERRID(x) ((x) << 16)
+#define FSPI_STS1_AHB_ERRCD(x) ((x) << 8)
+#define FSPI_STS1_AHB_ERRID(x) (x)
+
+#define FSPI_AHBSPNST 0xEC
+#define FSPI_AHBSPNST_DATLFT(x) ((x) << 16)
+#define FSPI_AHBSPNST_BUFID(x) ((x) << 1)
+#define FSPI_AHBSPNST_ACTIVE BIT(0)
+
+#define FSPI_IPRXFSTS 0xF0
+#define FSPI_IPRXFSTS_RDCNTR(x) ((x) << 16)
+#define FSPI_IPRXFSTS_FILL(x) (x)
+
+#define FSPI_IPTXFSTS 0xF4
+#define FSPI_IPTXFSTS_WRCNTR(x) ((x) << 16)
+#define FSPI_IPTXFSTS_FILL(x) (x)
+
+#define FSPI_RFDR 0x100
+#define FSPI_TFDR 0x180
+
+#define FSPI_LUT_BASE 0x200
+#define FSPI_LUT_OFFSET (SEQID_LUT * 4 * 4)
+#define FSPI_LUT_REG(idx) \
+ (FSPI_LUT_BASE + FSPI_LUT_OFFSET + (idx) * 4)
+
+/* register map end */
+
+/* Instruction set for the LUT register. */
+#define LUT_STOP 0x00
+#define LUT_CMD 0x01
+#define LUT_ADDR 0x02
+#define LUT_CADDR_SDR 0x03
+#define LUT_MODE 0x04
+#define LUT_MODE2 0x05
+#define LUT_MODE4 0x06
+#define LUT_MODE8 0x07
+#define LUT_NXP_WRITE 0x08
+#define LUT_NXP_READ 0x09
+#define LUT_LEARN_SDR 0x0A
+#define LUT_DATSZ_SDR 0x0B
+#define LUT_DUMMY 0x0C
+#define LUT_DUMMY_RWDS_SDR 0x0D
+#define LUT_JMP_ON_CS 0x1F
+#define LUT_CMD_DDR 0x21
+#define LUT_ADDR_DDR 0x22
+#define LUT_CADDR_DDR 0x23
+#define LUT_MODE_DDR 0x24
+#define LUT_MODE2_DDR 0x25
+#define LUT_MODE4_DDR 0x26
+#define LUT_MODE8_DDR 0x27
+#define LUT_WRITE_DDR 0x28
+#define LUT_READ_DDR 0x29
+#define LUT_LEARN_DDR 0x2A
+#define LUT_DATSZ_DDR 0x2B
+#define LUT_DUMMY_DDR 0x2C
+#define LUT_DUMMY_RWDS_DDR 0x2D
+
+/*
+ * Calculate number of required PAD bits for LUT register.
+ *
+ * The pad stands for the number of IO lines [0:7].
+ * For example, the octal read needs eight IO lines,
+ * so you should use LUT_PAD(8). This macro
+ * returns 3 i.e. use eight (2^3) IP lines for read.
+ */
+#define LUT_PAD(x) (fls(x) - 1)
+
+/*
+ * Macro for constructing the LUT entries with the following
+ * register layout:
+ *
+ * ---------------------------------------------------
+ * | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
+ * ---------------------------------------------------
+ */
+#define PAD_SHIFT 8
+#define INSTR_SHIFT 10
+#define OPRND_SHIFT 16
+
+/* Macros for constructing the LUT register. */
+#define LUT_DEF(idx, ins, pad, opr) \
+ ((((ins) << INSTR_SHIFT) | ((pad) << PAD_SHIFT) | \
+ (opr)) << (((idx) % 2) * OPRND_SHIFT))
+
+#define POLL_TOUT 5000
+#define NXP_FSPI_MAX_CHIPSELECT 4
+#define NXP_FSPI_MIN_IOMAP SZ_4M
+
+#define DCFG_RCWSR1 0x100
+
+/* Access flash memory using IP bus only */
+#define FSPI_QUIRK_USE_IP_ONLY BIT(0)
+
+struct nxp_fspi_devtype_data {
+ unsigned int rxfifo;
+ unsigned int txfifo;
+ unsigned int ahb_buf_size;
+ unsigned int quirks;
+ bool little_endian;
+};
+
+static struct nxp_fspi_devtype_data imx8mm_data = {
+ .rxfifo = SZ_512, /* (64 * 64 bits) */
+ .txfifo = SZ_1K, /* (128 * 64 bits) */
+ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
+ .quirks = 0,
+ .little_endian = true, /* little-endian */
+};
+
+static struct nxp_fspi_devtype_data imx8qxp_data = {
+ .rxfifo = SZ_512, /* (64 * 64 bits) */
+ .txfifo = SZ_1K, /* (128 * 64 bits) */
+ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
+ .quirks = 0,
+ .little_endian = true, /* little-endian */
+};
+
+static struct nxp_fspi_devtype_data imx8dxl_data = {
+ .rxfifo = SZ_512, /* (64 * 64 bits) */
+ .txfifo = SZ_1K, /* (128 * 64 bits) */
+ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
+ .quirks = FSPI_QUIRK_USE_IP_ONLY,
+ .little_endian = true, /* little-endian */
+};
+
+struct nxp_fspi {
+ void __iomem *iobase;
+ void __iomem *ahb_addr;
+ u32 memmap_phy;
+ u32 memmap_phy_size;
+ u32 memmap_start;
+ u32 memmap_len;
+ struct clk *clk, *clk_en;
+ struct device *dev;
+ struct spi_controller ctlr;
+ const struct nxp_fspi_devtype_data *devtype_data;
+ struct mutex lock;
+ int selected;
+};
+
+static inline int needs_ip_only(struct nxp_fspi *f)
+{
+ return f->devtype_data->quirks & FSPI_QUIRK_USE_IP_ONLY;
+}
+
+/*
+ * R/W functions for big- or little-endian registers:
+ * The FSPI controller's endianness is independent of
+ * the CPU core's endianness. So far, although the CPU
+ * core is little-endian the FSPI controller can use
+ * big-endian or little-endian.
+ */
+static void fspi_writel(struct nxp_fspi *f, u32 val, void __iomem *addr)
+{
+ if (f->devtype_data->little_endian)
+ iowrite32(val, addr);
+ else
+ iowrite32be(val, addr);
+}
+
+static u32 fspi_readl(struct nxp_fspi *f, void __iomem *addr)
+{
+ if (f->devtype_data->little_endian)
+ return ioread32(addr);
+ else
+ return ioread32be(addr);
+}
+
+static int nxp_fspi_check_buswidth(struct nxp_fspi *f, u8 width)
+{
+ switch (width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ return 0;
+ }
+
+ return -ENOTSUPP;
+}
+
+static bool nxp_fspi_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->master);
+ int ret;
+
+ ret = nxp_fspi_check_buswidth(f, op->cmd.buswidth);
+
+ if (op->addr.nbytes)
+ ret |= nxp_fspi_check_buswidth(f, op->addr.buswidth);
+
+ if (op->dummy.nbytes)
+ ret |= nxp_fspi_check_buswidth(f, op->dummy.buswidth);
+
+ if (op->data.nbytes)
+ ret |= nxp_fspi_check_buswidth(f, op->data.buswidth);
+
+ if (ret)
+ return false;
+
+ /*
+ * The number of address bytes should be equal to or less than 4 bytes.
+ */
+ if (op->addr.nbytes > 4)
+ return false;
+
+ /*
+ * If requested address value is greater than controller assigned
+ * memory mapped space, return error as it didn't fit in the range
+ * of assigned address space.
+ */
+ if (op->addr.val >= f->memmap_phy_size)
+ return false;
+
+ /* Max 64 dummy clock cycles supported */
+ if (op->dummy.buswidth &&
+ (op->dummy.nbytes * 8 / op->dummy.buswidth > 64))
+ return false;
+
+ /* Max data length, check controller limits and alignment */
+ if (op->data.dir == SPI_MEM_DATA_IN &&
+ (op->data.nbytes > f->devtype_data->ahb_buf_size ||
+ (op->data.nbytes > f->devtype_data->rxfifo - 4 &&
+ !IS_ALIGNED(op->data.nbytes, 8))))
+ return false;
+
+ if (op->data.dir == SPI_MEM_DATA_OUT &&
+ op->data.nbytes > f->devtype_data->txfifo)
+ return false;
+
+ return true;
+}
+
+/* Instead of busy looping invoke readl_poll_timeout functionality. */
+static int fspi_readl_poll_tout(struct nxp_fspi *f, void __iomem *base,
+ u32 mask, u32 delay_us,
+ u32 timeout_us, bool c)
+{
+ u32 reg = 0;
+
+ if (!f->devtype_data->little_endian)
+ mask = (u32)cpu_to_be32(mask);
+
+ if (c)
+ return readl_poll_timeout(base, reg, (reg & mask), timeout_us);
+ else
+ return readl_poll_timeout(base, reg, !(reg & mask), timeout_us);
+}
+
+/*
+ * If the slave device content being changed by Write/Erase, need to
+ * invalidate the AHB buffer. This can be achieved by doing the reset
+ * of controller after setting MCR0[SWRESET] bit.
+ */
+static inline void nxp_fspi_invalid(struct nxp_fspi *f)
+{
+ u32 reg;
+ int ret;
+
+ reg = fspi_readl(f, f->iobase + FSPI_MCR0);
+ fspi_writel(f, reg | FSPI_MCR0_SWRST, f->iobase + FSPI_MCR0);
+
+ /* w1c register, wait unit clear */
+ ret = fspi_readl_poll_tout(f, f->iobase + FSPI_MCR0,
+ FSPI_MCR0_SWRST, 0, POLL_TOUT, false);
+ WARN_ON(ret);
+}
+
+static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
+ const struct spi_mem_op *op)
+{
+ void __iomem *base = f->iobase;
+ u32 lutval[4] = {};
+ int lutidx = 1, i;
+
+ /* cmd */
+ lutval[0] |= LUT_DEF(0, LUT_CMD, LUT_PAD(op->cmd.buswidth),
+ op->cmd.opcode);
+
+ /* addr bytes */
+ if (op->addr.nbytes) {
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_ADDR,
+ LUT_PAD(op->addr.buswidth),
+ op->addr.nbytes * 8);
+ lutidx++;
+ }
+
+ /* dummy bytes, if needed */
+ if (op->dummy.nbytes) {
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_DUMMY,
+ /*
+ * Due to FlexSPI controller limitation number of PAD for dummy
+ * buswidth needs to be programmed as equal to data buswidth.
+ */
+ LUT_PAD(op->data.buswidth),
+ op->dummy.nbytes * 8 /
+ op->dummy.buswidth);
+ lutidx++;
+ }
+
+ /* read/write data bytes */
+ if (op->data.nbytes) {
+ lutval[lutidx / 2] |= LUT_DEF(lutidx,
+ op->data.dir == SPI_MEM_DATA_IN ?
+ LUT_NXP_READ : LUT_NXP_WRITE,
+ LUT_PAD(op->data.buswidth),
+ 0);
+ lutidx++;
+ }
+
+ /* stop condition. */
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_STOP, 0, 0);
+
+ /* unlock LUT */
+ fspi_writel(f, FSPI_LUTKEY_VALUE, f->iobase + FSPI_LUTKEY);
+ fspi_writel(f, FSPI_LCKER_UNLOCK, f->iobase + FSPI_LCKCR);
+
+ /* fill LUT */
+ for (i = 0; i < ARRAY_SIZE(lutval); i++)
+ fspi_writel(f, lutval[i], base + FSPI_LUT_REG(i));
+
+ dev_dbg((const struct device *)f->dev,
+ "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n",
+ op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3],
+ op->data.nbytes);
+
+ /* lock LUT */
+ fspi_writel(f, FSPI_LUTKEY_VALUE, f->iobase + FSPI_LUTKEY);
+ fspi_writel(f, FSPI_LCKER_LOCK, f->iobase + FSPI_LCKCR);
+}
+
+static int nxp_fspi_clk_prep_enable(struct nxp_fspi *f)
+{
+ int ret;
+
+ ret = clk_enable(f->clk_en);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(f->clk);
+ if (ret) {
+ clk_disable(f->clk_en);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nxp_fspi_clk_disable_unprep(struct nxp_fspi *f)
+{
+ clk_disable(f->clk);
+ clk_disable(f->clk_en);
+
+ return 0;
+}
+
+/*
+ * In FlexSPI controller, flash access is based on value of FSPI_FLSHXXCR0
+ * register and start base address of the slave device.
+ *
+ * (Higher address)
+ * -------- <-- FLSHB2CR0
+ * | B2 |
+ * | |
+ * B2 start address --> -------- <-- FLSHB1CR0
+ * | B1 |
+ * | |
+ * B1 start address --> -------- <-- FLSHA2CR0
+ * | A2 |
+ * | |
+ * A2 start address --> -------- <-- FLSHA1CR0
+ * | A1 |
+ * | |
+ * A1 start address --> -------- (Lower address)
+ *
+ *
+ * Start base address defines the starting address range for given CS and
+ * FSPI_FLSHXXCR0 defines the size of the slave device connected at given CS.
+ *
+ * But, different targets are having different combinations of number of CS,
+ * some targets only have single CS or two CS covering controller's full
+ * memory mapped space area.
+ * Thus, implementation is being done as independent of the size and number
+ * of the connected slave device.
+ * Assign controller memory mapped space size as the size to the connected
+ * slave device.
+ * Mark FLSHxxCR0 as zero initially and then assign value only to the selected
+ * chip-select Flash configuration register.
+ *
+ * For e.g. to access CS2 (B1), FLSHB1CR0 register would be equal to the
+ * memory mapped size of the controller.
+ * Value for rest of the CS FLSHxxCR0 register would be zero.
+ *
+ */
+static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi)
+{
+ unsigned long rate = spi->max_speed_hz;
+ int ret;
+ uint64_t size_kb;
+
+ /*
+ * Return, if previously selected slave device is same as current
+ * requested slave device.
+ */
+ if (f->selected == spi->chip_select)
+ return;
+
+ /* Reset FLSHxxCR0 registers */
+ fspi_writel(f, 0, f->iobase + FSPI_FLSHA1CR0);
+ fspi_writel(f, 0, f->iobase + FSPI_FLSHA2CR0);
+ fspi_writel(f, 0, f->iobase + FSPI_FLSHB1CR0);
+ fspi_writel(f, 0, f->iobase + FSPI_FLSHB2CR0);
+
+ /* Assign controller memory mapped space as size, KBytes, of flash. */
+ size_kb = FSPI_FLSHXCR0_SZ(f->memmap_phy_size);
+
+ fspi_writel(f, size_kb, f->iobase + FSPI_FLSHA1CR0 +
+ 4 * spi->chip_select);
+
+ dev_dbg((const struct device *)f->dev,
+ "Slave device [CS:%x] selected\n",
+ spi->chip_select);
+
+ nxp_fspi_clk_disable_unprep(f);
+
+ ret = clk_set_rate(f->clk, rate);
+ if (ret)
+ return;
+
+ ret = nxp_fspi_clk_prep_enable(f);
+ if (ret)
+ return;
+
+ f->selected = spi->chip_select;
+}
+
+static int nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op)
+{
+ /* Read out the data directly from the AHB buffer. */
+ memcpy_fromio(op->data.buf.in,
+ f->ahb_addr + op->addr.val - f->memmap_start, op->data.nbytes);
+
+ return 0;
+}
+
+static void nxp_fspi_fill_txfifo(struct nxp_fspi *f,
+ const struct spi_mem_op *op)
+{
+ void __iomem *base = f->iobase;
+ int i, ret;
+ u8 *buf = (u8 *) op->data.buf.out;
+
+ /* clear the TX FIFO. */
+ fspi_writel(f, FSPI_IPTXFCR_CLR, base + FSPI_IPTXFCR);
+
+ /*
+ * Default value of water mark level is 8 bytes, hence in single
+ * write request controller can write max 8 bytes of data.
+ */
+
+ for (i = 0; i < ALIGN_DOWN(op->data.nbytes, 8); i += 8) {
+ /* Wait for TXFIFO empty */
+ ret = fspi_readl_poll_tout(f, f->iobase + FSPI_INTR,
+ FSPI_INTR_IPTXWE, 0,
+ POLL_TOUT, true);
+ WARN_ON(ret);
+
+ fspi_writel(f, *(u32 *) (buf + i), base + FSPI_TFDR);
+ fspi_writel(f, *(u32 *) (buf + i + 4), base + FSPI_TFDR + 4);
+ fspi_writel(f, FSPI_INTR_IPTXWE, base + FSPI_INTR);
+ }
+
+ if (i < op->data.nbytes) {
+ u32 data = 0;
+ int j;
+ /* Wait for TXFIFO empty */
+ ret = fspi_readl_poll_tout(f, f->iobase + FSPI_INTR,
+ FSPI_INTR_IPTXWE, 0,
+ POLL_TOUT, true);
+ WARN_ON(ret);
+
+ for (j = 0; j < ALIGN(op->data.nbytes - i, 4); j += 4) {
+ memcpy(&data, buf + i + j, 4);
+ fspi_writel(f, data, base + FSPI_TFDR + j);
+ }
+ fspi_writel(f, FSPI_INTR_IPTXWE, base + FSPI_INTR);
+ }
+}
+
+static void nxp_fspi_read_rxfifo(struct nxp_fspi *f,
+ const struct spi_mem_op *op)
+{
+ void __iomem *base = f->iobase;
+ int i, ret;
+ int len = op->data.nbytes;
+ u8 *buf = (u8 *) op->data.buf.in;
+
+ /*
+ * Default value of water mark level is 8 bytes, hence in single
+ * read request controller can read max 8 bytes of data.
+ */
+ for (i = 0; i < ALIGN_DOWN(len, 8); i += 8) {
+ /* Wait for RXFIFO available */
+ ret = fspi_readl_poll_tout(f, f->iobase + FSPI_INTR,
+ FSPI_INTR_IPRXWA, 0,
+ POLL_TOUT, true);
+ WARN_ON(ret);
+
+ *(u32 *)(buf + i) = fspi_readl(f, base + FSPI_RFDR);
+ *(u32 *)(buf + i + 4) = fspi_readl(f, base + FSPI_RFDR + 4);
+ /* move the FIFO pointer */
+ fspi_writel(f, FSPI_INTR_IPRXWA, base + FSPI_INTR);
+ }
+
+ if (i < len) {
+ u32 tmp;
+ int size, j;
+
+ buf = op->data.buf.in + i;
+ /* Wait for RXFIFO available */
+ ret = fspi_readl_poll_tout(f, f->iobase + FSPI_INTR,
+ FSPI_INTR_IPRXWA, 0,
+ POLL_TOUT, true);
+ WARN_ON(ret);
+
+ len = op->data.nbytes - i;
+ for (j = 0; j < op->data.nbytes - i; j += 4) {
+ tmp = fspi_readl(f, base + FSPI_RFDR + j);
+ size = min(len, 4);
+ memcpy(buf + j, &tmp, size);
+ len -= size;
+ }
+ }
+
+ /* invalid the RXFIFO */
+ fspi_writel(f, FSPI_IPRXFCR_CLR, base + FSPI_IPRXFCR);
+ /* move the FIFO pointer */
+ fspi_writel(f, FSPI_INTR_IPRXWA, base + FSPI_INTR);
+}
+
+static int nxp_fspi_do_op(struct nxp_fspi *f, const struct spi_mem_op *op)
+{
+ void __iomem *base = f->iobase;
+ int seqnum = 0;
+ int err = 0;
+ u32 reg;
+
+ reg = fspi_readl(f, base + FSPI_IPRXFCR);
+ /* invalid RXFIFO first */
+ reg &= ~FSPI_IPRXFCR_DMA_EN;
+ reg = reg | FSPI_IPRXFCR_CLR;
+ fspi_writel(f, reg, base + FSPI_IPRXFCR);
+
+ fspi_writel(f, op->addr.val, base + FSPI_IPCR0);
+ /*
+ * Always start the sequence at the same index since we update
+ * the LUT at each exec_op() call. And also specify the DATA
+ * length, since it's has not been specified in the LUT.
+ */
+ fspi_writel(f, op->data.nbytes |
+ (SEQID_LUT << FSPI_IPCR1_SEQID_SHIFT) |
+ (seqnum << FSPI_IPCR1_SEQNUM_SHIFT),
+ base + FSPI_IPCR1);
+
+ /* Trigger the LUT now. */
+ fspi_writel(f, FSPI_IPCMD_TRG, base + FSPI_IPCMD);
+
+ /* Wait for the completion. */
+ err = fspi_readl_poll_tout(f, f->iobase + FSPI_STS0,
+ FSPI_STS0_ARB_IDLE, 1, 1000 * 1000, true);
+
+ /* Invoke IP data read, if request is of data read. */
+ if (!err && op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN)
+ nxp_fspi_read_rxfifo(f, op);
+
+ return err;
+}
+
+static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+ struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->master);
+ int err = 0;
+
+ mutex_lock(&f->lock);
+
+ /* Wait for controller being ready. */
+ err = fspi_readl_poll_tout(f, f->iobase + FSPI_STS0,
+ FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, true);
+ WARN_ON(err);
+
+ nxp_fspi_select_mem(f, mem->spi);
+
+ nxp_fspi_prepare_lut(f, op);
+ /*
+ * If we have large chunks of data, we read them through the AHB bus by
+ * accessing the mapped memory. In all other cases we use IP commands
+ * to access the flash. Read via AHB bus may be corrupted due to
+ * existence of an errata and therefore discard AHB read in such cases.
+ */
+ if (op->data.nbytes > (f->devtype_data->rxfifo - 4) &&
+ op->data.dir == SPI_MEM_DATA_IN &&
+ !needs_ip_only(f)) {
+ err = nxp_fspi_read_ahb(f, op);
+ } else {
+ if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
+ nxp_fspi_fill_txfifo(f, op);
+
+ err = nxp_fspi_do_op(f, op);
+ }
+
+ /* Invalidate the data in the AHB buffer. */
+ nxp_fspi_invalid(f);
+
+ mutex_unlock(&f->lock);
+
+ return err;
+}
+
+static int nxp_fspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+ struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->master);
+
+ if (op->data.dir == SPI_MEM_DATA_OUT) {
+ if (op->data.nbytes > f->devtype_data->txfifo)
+ op->data.nbytes = f->devtype_data->txfifo;
+ } else {
+ if (op->data.nbytes > f->devtype_data->ahb_buf_size)
+ op->data.nbytes = f->devtype_data->ahb_buf_size;
+ else if (op->data.nbytes > (f->devtype_data->rxfifo - 4))
+ op->data.nbytes = ALIGN_DOWN(op->data.nbytes, 8);
+ }
+
+ /* Limit data bytes to RX FIFO in case of IP read only */
+ if (op->data.dir == SPI_MEM_DATA_IN &&
+ needs_ip_only(f) &&
+ op->data.nbytes > f->devtype_data->rxfifo)
+ op->data.nbytes = f->devtype_data->rxfifo;
+
+ return 0;
+}
+
+static int nxp_fspi_setup(struct spi_device *spi)
+{
+ struct nxp_fspi *f = container_of(spi->controller, struct nxp_fspi, ctlr);
+ void __iomem *base = f->iobase;
+ int ret, i;
+ u32 reg;
+
+ /* disable and unprepare clock to avoid glitch pass to controller */
+ nxp_fspi_clk_disable_unprep(f);
+
+ /* the default frequency, we will change it later if necessary. */
+ ret = clk_set_rate(f->clk, 20000000);
+ if (ret)
+ return ret;
+
+ ret = nxp_fspi_clk_prep_enable(f);
+ if (ret)
+ return ret;
+
+ /* Reset the module */
+ /* w1c register, wait unit clear */
+ ret = fspi_readl_poll_tout(f, f->iobase + FSPI_MCR0,
+ FSPI_MCR0_SWRST, 0, POLL_TOUT, false);
+ WARN_ON(ret);
+
+ /* Disable the module */
+ fspi_writel(f, FSPI_MCR0_MDIS, base + FSPI_MCR0);
+
+ /* Reset the DLL register to default value */
+ fspi_writel(f, FSPI_DLLACR_OVRDEN, base + FSPI_DLLACR);
+ fspi_writel(f, FSPI_DLLBCR_OVRDEN, base + FSPI_DLLBCR);
+
+ /* enable module */
+ fspi_writel(f, FSPI_MCR0_AHB_TIMEOUT(0xFF) |
+ FSPI_MCR0_IP_TIMEOUT(0xFF) | (u32) FSPI_MCR0_OCTCOMB_EN,
+ base + FSPI_MCR0);
+
+ /*
+ * Disable same device enable bit and configure all slave devices
+ * independently.
+ */
+ reg = fspi_readl(f, f->iobase + FSPI_MCR2);
+ reg = reg & ~(FSPI_MCR2_SAMEDEVICEEN);
+ fspi_writel(f, reg, base + FSPI_MCR2);
+
+ /* AHB configuration for access buffer 0~7. */
+ for (i = 0; i < 7; i++)
+ fspi_writel(f, 0, base + FSPI_AHBRX_BUF0CR0 + 4 * i);
+
+ /*
+ * Set ADATSZ with the maximum AHB buffer size to improve the read
+ * performance.
+ */
+ fspi_writel(f, (f->devtype_data->ahb_buf_size / 8 |
+ FSPI_AHBRXBUF0CR7_PREF), base + FSPI_AHBRX_BUF7CR0);
+
+ /* prefetch and no start address alignment limitation */
+ fspi_writel(f, FSPI_AHBCR_PREF_EN | FSPI_AHBCR_RDADDROPT,
+ base + FSPI_AHBCR);
+
+ /* AHB Read - Set lut sequence ID for all CS. */
+ fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA1CR2);
+ fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA2CR2);
+ fspi_writel(f, SEQID_LUT, base + FSPI_FLSHB1CR2);
+ fspi_writel(f, SEQID_LUT, base + FSPI_FLSHB2CR2);
+
+ f->selected = -1;
+
+ return 0;
+}
+
+static const char *nxp_fspi_get_name(struct spi_mem *mem)
+{
+ struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->master);
+ struct device *dev = (struct device *)&mem->spi->dev;
+ const char *name;
+
+ /* Set custom name derived from the platform_device of the controller.
+ */
+ if (of_get_available_child_count(f->dev->of_node) == 1)
+ return dev_name(f->dev);
+
+ name = basprintf("%s-%d", dev_name(f->dev), mem->spi->chip_select);
+ if (!name) {
+ dev_err(dev, "failed to get memory for custom flash name\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return name;
+}
+
+static const struct spi_controller_mem_ops nxp_fspi_mem_ops = {
+ .adjust_op_size = nxp_fspi_adjust_op_size,
+ .supports_op = nxp_fspi_supports_op,
+ .exec_op = nxp_fspi_exec_op,
+ .get_name = nxp_fspi_get_name,
+};
+
+static int nxp_fspi_probe(struct device *dev)
+{
+ struct spi_controller *ctlr;
+ struct resource *res;
+ struct nxp_fspi *f;
+ int ret;
+
+ f = xzalloc(sizeof(*f));
+
+ f->dev = dev;
+ f->devtype_data = of_device_get_match_data(dev);
+ if (!f->devtype_data) {
+ ret = -ENODEV;
+ goto err_put_ctrl;
+ }
+
+ ctlr = &f->ctlr;
+
+ /* ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL | \ */
+ /* SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL; */
+
+ ctlr->dev = dev;
+ ctlr->bus_num = dev->id;
+ ctlr->setup = nxp_fspi_setup;
+ ctlr->num_chipselect = NXP_FSPI_MAX_CHIPSELECT;
+ ctlr->mem_ops = &nxp_fspi_mem_ops;
+
+ spi_controller_set_devdata(ctlr, f);
+
+ /* find the resources */
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
+ goto err_put_ctrl;
+ }
+
+ f->iobase = IOMEM(res->start);
+
+ res = dev_request_mem_resource(dev, 1);
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
+ goto err_put_ctrl;
+ }
+ f->ahb_addr = IOMEM(res->start);
+
+ /* assign memory mapped starting address and mapped size. */
+ f->memmap_phy = res->start;
+ f->memmap_phy_size = resource_size(res);
+
+ /* find the clocks */
+ f->clk_en = clk_get(dev, "fspi_en");
+ if (IS_ERR(f->clk_en)) {
+ ret = PTR_ERR(f->clk_en);
+ goto err_put_ctrl;
+ }
+
+ f->clk = clk_get(dev, "fspi");
+ if (IS_ERR(f->clk)) {
+ ret = PTR_ERR(f->clk);
+ goto err_put_ctrl;
+ }
+
+ ret = nxp_fspi_clk_prep_enable(f);
+ if (ret) {
+ dev_err(dev, "can not enable the clock\n");
+ goto err_put_ctrl;
+ }
+
+ mutex_init(&f->lock);
+
+ ret = spi_register_controller(ctlr);
+ if (ret)
+ goto err_disable_clk;
+
+ return 0;
+
+err_disable_clk:
+ nxp_fspi_clk_disable_unprep(f);
+
+err_put_ctrl:
+ dev_err(dev, "NXP FSPI probe failed\n");
+ return ret;
+}
+
+static const struct of_device_id nxp_fspi_dt_ids[] = {
+ { .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, },
+ { .compatible = "nxp,imx8mn-fspi", .data = (void *)&imx8mm_data, },
+ { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, },
+ { .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, },
+ { .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids);
+
+static struct driver nxp_fspi_driver = {
+ .name = "nxp-fspi",
+ .probe = nxp_fspi_probe,
+ .of_compatible = DRV_OF_COMPAT(nxp_fspi_dt_ids),
+};
+device_platform_driver(nxp_fspi_driver);
diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c
new file mode 100644
index 0000000000..fbe80718e4
--- /dev/null
+++ b/drivers/spi/spi-sifive.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 SiFive, Inc.
+ * Copyright 2019 Bhargav Shah <bhargavshah1988@gmail.com>
+ *
+ * SiFive SPI controller driver (master mode only)
+ */
+
+#include <common.h>
+#include <linux/clk.h>
+#include <driver.h>
+#include <init.h>
+#include <errno.h>
+#include <linux/reset.h>
+#include <spi/spi.h>
+#include <linux/spi/spi-mem.h>
+#include <linux/bitops.h>
+#include <clock.h>
+#include <gpio.h>
+#include <of_gpio.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <linux/log2.h>
+
+#define SIFIVE_SPI_MAX_CS 32
+
+#define SIFIVE_SPI_DEFAULT_DEPTH 8
+#define SIFIVE_SPI_DEFAULT_BITS 8
+
+/* register offsets */
+#define SIFIVE_SPI_REG_SCKDIV 0x00 /* Serial clock divisor */
+#define SIFIVE_SPI_REG_SCKMODE 0x04 /* Serial clock mode */
+#define SIFIVE_SPI_REG_CSID 0x10 /* Chip select ID */
+#define SIFIVE_SPI_REG_CSDEF 0x14 /* Chip select default */
+#define SIFIVE_SPI_REG_CSMODE 0x18 /* Chip select mode */
+#define SIFIVE_SPI_REG_DELAY0 0x28 /* Delay control 0 */
+#define SIFIVE_SPI_REG_DELAY1 0x2c /* Delay control 1 */
+#define SIFIVE_SPI_REG_FMT 0x40 /* Frame format */
+#define SIFIVE_SPI_REG_TXDATA 0x48 /* Tx FIFO data */
+#define SIFIVE_SPI_REG_RXDATA 0x4c /* Rx FIFO data */
+#define SIFIVE_SPI_REG_TXMARK 0x50 /* Tx FIFO watermark */
+#define SIFIVE_SPI_REG_RXMARK 0x54 /* Rx FIFO watermark */
+#define SIFIVE_SPI_REG_FCTRL 0x60 /* SPI flash interface control */
+#define SIFIVE_SPI_REG_FFMT 0x64 /* SPI flash instruction format */
+#define SIFIVE_SPI_REG_IE 0x70 /* Interrupt Enable Register */
+#define SIFIVE_SPI_REG_IP 0x74 /* Interrupt Pendings Register */
+
+/* sckdiv bits */
+#define SIFIVE_SPI_SCKDIV_DIV_MASK 0xfffU
+
+/* sckmode bits */
+#define SIFIVE_SPI_SCKMODE_PHA BIT(0)
+#define SIFIVE_SPI_SCKMODE_POL BIT(1)
+#define SIFIVE_SPI_SCKMODE_MODE_MASK (SIFIVE_SPI_SCKMODE_PHA | \
+ SIFIVE_SPI_SCKMODE_POL)
+
+/* csmode bits */
+#define SIFIVE_SPI_CSMODE_MODE_AUTO 0U
+#define SIFIVE_SPI_CSMODE_MODE_HOLD 2U
+#define SIFIVE_SPI_CSMODE_MODE_OFF 3U
+
+/* delay0 bits */
+#define SIFIVE_SPI_DELAY0_CSSCK(x) ((u32)(x))
+#define SIFIVE_SPI_DELAY0_CSSCK_MASK 0xffU
+#define SIFIVE_SPI_DELAY0_SCKCS(x) ((u32)(x) << 16)
+#define SIFIVE_SPI_DELAY0_SCKCS_MASK (0xffU << 16)
+
+/* delay1 bits */
+#define SIFIVE_SPI_DELAY1_INTERCS(x) ((u32)(x))
+#define SIFIVE_SPI_DELAY1_INTERCS_MASK 0xffU
+#define SIFIVE_SPI_DELAY1_INTERXFR(x) ((u32)(x) << 16)
+#define SIFIVE_SPI_DELAY1_INTERXFR_MASK (0xffU << 16)
+
+/* fmt bits */
+#define SIFIVE_SPI_FMT_PROTO_SINGLE 0U
+#define SIFIVE_SPI_FMT_PROTO_DUAL 1U
+#define SIFIVE_SPI_FMT_PROTO_QUAD 2U
+#define SIFIVE_SPI_FMT_PROTO_MASK 3U
+#define SIFIVE_SPI_FMT_ENDIAN BIT(2)
+#define SIFIVE_SPI_FMT_DIR BIT(3)
+#define SIFIVE_SPI_FMT_LEN(x) ((u32)(x) << 16)
+#define SIFIVE_SPI_FMT_LEN_MASK (0xfU << 16)
+
+/* txdata bits */
+#define SIFIVE_SPI_TXDATA_DATA_MASK 0xffU
+#define SIFIVE_SPI_TXDATA_FULL BIT(31)
+
+/* rxdata bits */
+#define SIFIVE_SPI_RXDATA_DATA_MASK 0xffU
+#define SIFIVE_SPI_RXDATA_EMPTY BIT(31)
+
+/* ie and ip bits */
+#define SIFIVE_SPI_IP_TXWM BIT(0)
+#define SIFIVE_SPI_IP_RXWM BIT(1)
+
+/* format protocol */
+#define SIFIVE_SPI_PROTO_QUAD 4 /* 4 lines I/O protocol transfer */
+#define SIFIVE_SPI_PROTO_DUAL 2 /* 2 lines I/O protocol transfer */
+#define SIFIVE_SPI_PROTO_SINGLE 1 /* 1 line I/O protocol transfer */
+
+#define SPI_XFER_BEGIN 0x01 /* Assert CS before transfer */
+#define SPI_XFER_END 0x02 /* Deassert CS after transfer */
+
+struct sifive_spi {
+ struct spi_controller ctlr;
+ void __iomem *regs; /* base address of the registers */
+ u32 fifo_depth;
+ u32 bits_per_word;
+ u32 cs_inactive; /* Level of the CS pins when inactive*/
+ u32 freq;
+ u8 fmt_proto;
+};
+
+static inline struct sifive_spi *to_sifive_spi(struct spi_controller *ctlr)
+{
+ return container_of(ctlr, struct sifive_spi, ctlr);
+}
+
+static void sifive_spi_prep_device(struct sifive_spi *spi,
+ struct spi_device *spi_dev)
+{
+ /* Update the chip select polarity */
+ if (spi_dev->mode & SPI_CS_HIGH)
+ spi->cs_inactive &= ~BIT(spi_dev->chip_select);
+ else
+ spi->cs_inactive |= BIT(spi_dev->chip_select);
+ writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
+
+ /* Select the correct device */
+ writel(spi_dev->chip_select, spi->regs + SIFIVE_SPI_REG_CSID);
+}
+
+static void sifive_spi_set_cs(struct sifive_spi *spi,
+ struct spi_device *spi_dev)
+{
+ u32 cs_mode = SIFIVE_SPI_CSMODE_MODE_HOLD;
+
+ if (spi_dev->mode & SPI_CS_HIGH)
+ cs_mode = SIFIVE_SPI_CSMODE_MODE_AUTO;
+
+ writel(cs_mode, spi->regs + SIFIVE_SPI_REG_CSMODE);
+}
+
+static void sifive_spi_clear_cs(struct sifive_spi *spi)
+{
+ writel(SIFIVE_SPI_CSMODE_MODE_AUTO, spi->regs + SIFIVE_SPI_REG_CSMODE);
+}
+
+static void sifive_spi_prep_transfer(struct sifive_spi *spi,
+ struct spi_device *spi_dev,
+ u8 *rx_ptr)
+{
+ u32 cr;
+
+ /* Modify the SPI protocol mode */
+ cr = readl(spi->regs + SIFIVE_SPI_REG_FMT);
+
+ /* Bits per word ? */
+ cr &= ~SIFIVE_SPI_FMT_LEN_MASK;
+ cr |= SIFIVE_SPI_FMT_LEN(spi->bits_per_word);
+
+ /* LSB first? */
+ cr &= ~SIFIVE_SPI_FMT_ENDIAN;
+ if (spi_dev->mode & SPI_LSB_FIRST)
+ cr |= SIFIVE_SPI_FMT_ENDIAN;
+
+ /* Number of wires ? */
+ cr &= ~SIFIVE_SPI_FMT_PROTO_MASK;
+ switch (spi->fmt_proto) {
+ case SIFIVE_SPI_PROTO_QUAD:
+ cr |= SIFIVE_SPI_FMT_PROTO_QUAD;
+ break;
+ case SIFIVE_SPI_PROTO_DUAL:
+ cr |= SIFIVE_SPI_FMT_PROTO_DUAL;
+ break;
+ default:
+ cr |= SIFIVE_SPI_FMT_PROTO_SINGLE;
+ break;
+ }
+
+ /* SPI direction in/out ? */
+ cr &= ~SIFIVE_SPI_FMT_DIR;
+ if (!rx_ptr)
+ cr |= SIFIVE_SPI_FMT_DIR;
+
+ writel(cr, spi->regs + SIFIVE_SPI_REG_FMT);
+}
+
+static void sifive_spi_rx(struct sifive_spi *spi, u8 *rx_ptr)
+{
+ u32 data;
+
+ do {
+ data = readl(spi->regs + SIFIVE_SPI_REG_RXDATA);
+ } while (data & SIFIVE_SPI_RXDATA_EMPTY);
+
+ if (rx_ptr)
+ *rx_ptr = data & SIFIVE_SPI_RXDATA_DATA_MASK;
+}
+
+static void sifive_spi_tx(struct sifive_spi *spi, const u8 *tx_ptr)
+{
+ u32 data;
+ u8 tx_data = (tx_ptr) ? *tx_ptr & SIFIVE_SPI_TXDATA_DATA_MASK :
+ SIFIVE_SPI_TXDATA_DATA_MASK;
+
+ do {
+ data = readl(spi->regs + SIFIVE_SPI_REG_TXDATA);
+ } while (data & SIFIVE_SPI_TXDATA_FULL);
+
+ writel(tx_data, spi->regs + SIFIVE_SPI_REG_TXDATA);
+}
+
+static int sifive_spi_wait(struct sifive_spi *spi, u32 mask)
+{
+ u32 val;
+
+ return readl_poll_timeout(spi->regs + SIFIVE_SPI_REG_IP, val,
+ (val & mask) == mask, 100 * USEC_PER_MSEC);
+}
+
+static int sifive_spi_transfer_one(struct spi_device *spi_dev, unsigned int nbytes,
+ const void *dout, void *din)
+{
+ struct sifive_spi *spi = to_sifive_spi(spi_dev->controller);
+ const u8 *tx_ptr = dout;
+ u8 *rx_ptr = din;
+ int ret;
+
+ sifive_spi_prep_transfer(spi, spi_dev, rx_ptr);
+
+ while (nbytes) {
+ unsigned int n_words = min(nbytes, spi->fifo_depth);
+ unsigned int tx_words, rx_words;
+
+ /* Enqueue n_words for transmission */
+ for (tx_words = 0; tx_words < n_words; tx_words++) {
+ if (!tx_ptr)
+ sifive_spi_tx(spi, NULL);
+ else
+ sifive_spi_tx(spi, tx_ptr++);
+ }
+
+ if (rx_ptr) {
+ /* Wait for transmission + reception to complete */
+ writel(n_words - 1, spi->regs + SIFIVE_SPI_REG_RXMARK);
+ ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_RXWM);
+ if (ret)
+ return ret;
+
+ /* Read out all the data from the RX FIFO */
+ for (rx_words = 0; rx_words < n_words; rx_words++)
+ sifive_spi_rx(spi, rx_ptr++);
+ } else {
+ /* Wait for transmission to complete */
+ ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_TXWM);
+ if (ret)
+ return ret;
+ }
+
+ nbytes -= n_words;
+ }
+
+ return 0;
+}
+
+static int sifive_spi_transfer(struct spi_device *spi_dev, struct spi_message *msg)
+{
+ struct spi_controller *ctlr = spi_dev->controller;
+ struct sifive_spi *spi = to_sifive_spi(ctlr);
+ struct spi_transfer *t;
+ int ret = 0;
+
+ if (list_empty(&msg->transfers))
+ return 0;
+
+ msg->actual_length = 0;
+
+ sifive_spi_prep_device(spi, spi_dev);
+ sifive_spi_set_cs(spi, spi_dev);
+
+ dev_dbg(ctlr->dev, "transfer start actual_length=%i\n", msg->actual_length);
+ list_for_each_entry(t, &msg->transfers, transfer_list) {
+ dev_dbg(ctlr->dev, " xfer %p: len %u tx %p rx %p\n",
+ t, t->len, t->tx_buf, t->rx_buf);
+
+ ret = sifive_spi_transfer_one(spi_dev, t->len,
+ t->tx_buf, t->rx_buf);
+ if (ret < 0)
+ goto out;
+ msg->actual_length += t->len;
+ }
+ dev_dbg(ctlr->dev, "transfer done actual_length=%i\n", msg->actual_length);
+
+out:
+ sifive_spi_clear_cs(spi);
+ return ret;
+}
+
+static int sifive_spi_exec_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct spi_device *spi_dev = mem->spi;
+ struct device *dev = &spi_dev->dev;
+ struct sifive_spi *spi = spi_controller_get_devdata(spi_dev->controller);
+ u8 opcode = op->cmd.opcode;
+ int ret;
+
+ spi->fmt_proto = op->cmd.buswidth;
+
+ sifive_spi_prep_device(spi, spi_dev);
+ sifive_spi_set_cs(spi, spi_dev);
+
+ /* send the opcode */
+ ret = sifive_spi_transfer_one(spi_dev, 1, &opcode, NULL);
+ if (ret < 0) {
+ dev_err(dev, "failed to xfer opcode\n");
+ goto out;
+ }
+
+ if (!op->addr.nbytes && !op->data.nbytes)
+ goto out;
+
+ /* send the addr + dummy */
+ if (op->addr.nbytes) {
+ int i, op_len = op->addr.nbytes + op->dummy.nbytes;
+ u8 *op_buf;
+
+ op_buf = malloc(op_len);
+ if (!op_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* fill address */
+ for (i = 0; i < op->addr.nbytes; i++)
+ op_buf[i] = op->addr.val >>
+ (8 * (op->addr.nbytes - i - 1));
+
+ /* fill dummy */
+ memset(op_buf + op->addr.nbytes, 0xff, op->dummy.nbytes);
+
+ spi->fmt_proto = op->addr.buswidth;
+
+ ret = sifive_spi_transfer_one(spi_dev, op_len, op_buf, NULL);
+ free(op_buf);
+ if (ret < 0) {
+ dev_err(dev, "failed to xfer addr + dummy\n");
+ goto out;
+ }
+ }
+
+ /* send/received the data */
+ if (op->data.nbytes) {
+ const void *tx_buf = NULL;
+ void *rx_buf = NULL;
+
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ rx_buf = op->data.buf.in;
+ else
+ tx_buf = op->data.buf.out;
+
+ spi->fmt_proto = op->data.buswidth;
+
+ ret = sifive_spi_transfer_one(spi_dev, op->data.nbytes,
+ tx_buf, rx_buf);
+ if (ret) {
+ dev_err(dev, "failed to xfer data\n");
+ goto out;
+ }
+ }
+
+out:
+ sifive_spi_clear_cs(spi);
+ return ret;
+}
+
+static void sifive_spi_set_speed(struct sifive_spi *spi, uint speed)
+{
+ u32 scale;
+
+ if (speed > spi->freq)
+ speed = spi->freq;
+
+ /* Cofigure max speed */
+ scale = (DIV_ROUND_UP(spi->freq >> 1, speed) - 1)
+ & SIFIVE_SPI_SCKDIV_DIV_MASK;
+ writel(scale, spi->regs + SIFIVE_SPI_REG_SCKDIV);
+}
+
+static void sifive_spi_set_mode(struct sifive_spi *spi, uint mode)
+{
+ u32 cr;
+
+ /* Switch clock mode bits */
+ cr = readl(spi->regs + SIFIVE_SPI_REG_SCKMODE) &
+ ~SIFIVE_SPI_SCKMODE_MODE_MASK;
+ if (mode & SPI_CPHA)
+ cr |= SIFIVE_SPI_SCKMODE_PHA;
+ if (mode & SPI_CPOL)
+ cr |= SIFIVE_SPI_SCKMODE_POL;
+
+ writel(cr, spi->regs + SIFIVE_SPI_REG_SCKMODE);
+}
+
+static int sifive_spi_setup(struct spi_device *spi_dev)
+{
+ struct sifive_spi *spi = to_sifive_spi(spi_dev->controller);
+
+ sifive_spi_set_mode(spi, spi_dev->mode);
+ sifive_spi_set_speed(spi, spi_dev->max_speed_hz);
+
+ return 0;
+}
+
+static void sifive_spi_init_hw(struct sifive_spi *spi)
+{
+ struct device *dev = spi->ctlr.dev;
+ u32 cs_bits;
+
+ /* probe the number of CS lines */
+ spi->cs_inactive = readl(spi->regs + SIFIVE_SPI_REG_CSDEF);
+ writel(0xffffffffU, spi->regs + SIFIVE_SPI_REG_CSDEF);
+ cs_bits = readl(spi->regs + SIFIVE_SPI_REG_CSDEF);
+ writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
+ if (!cs_bits) {
+ dev_warn(dev, "Could not auto probe CS lines\n");
+ return;
+ }
+
+ spi->ctlr.num_chipselect = ilog2(cs_bits) + 1;
+ if (spi->ctlr.num_chipselect > SIFIVE_SPI_MAX_CS) {
+ dev_warn(dev, "Invalid number of spi slaves\n");
+ return;
+ }
+
+ /* Watermark interrupts are disabled by default */
+ writel(0, spi->regs + SIFIVE_SPI_REG_IE);
+
+ /* Default watermark FIFO threshold values */
+ writel(1, spi->regs + SIFIVE_SPI_REG_TXMARK);
+ writel(0, spi->regs + SIFIVE_SPI_REG_RXMARK);
+
+ /* Set CS/SCK Delays and Inactive Time to defaults */
+ writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1),
+ spi->regs + SIFIVE_SPI_REG_DELAY0);
+ writel(SIFIVE_SPI_DELAY1_INTERCS(1) | SIFIVE_SPI_DELAY1_INTERXFR(0),
+ spi->regs + SIFIVE_SPI_REG_DELAY1);
+
+ /* Exit specialized memory-mapped SPI flash mode */
+ writel(0, spi->regs + SIFIVE_SPI_REG_FCTRL);
+}
+
+static const struct spi_controller_mem_ops sifive_spi_mem_ops = {
+ .exec_op = sifive_spi_exec_op,
+};
+
+static void sifive_spi_dt_probe(struct sifive_spi *spi)
+{
+ struct device_node *node = spi->ctlr.dev->of_node;
+
+ spi->fifo_depth = SIFIVE_SPI_DEFAULT_DEPTH;
+ of_property_read_u32(node, "sifive,fifo-depth", &spi->fifo_depth);
+
+ spi->bits_per_word = SIFIVE_SPI_DEFAULT_BITS;
+ of_property_read_u32(node, "sifive,max-bits-per-word", &spi->bits_per_word);
+}
+
+static int sifive_spi_probe(struct device *dev)
+{
+ struct sifive_spi *spi;
+ struct resource *iores;
+ struct spi_controller *ctlr;
+ struct clk *clkdev;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ spi = xzalloc(sizeof(*spi));
+
+ spi->regs = IOMEM(iores->start);
+ if (!spi->regs)
+ return -ENODEV;
+
+ ctlr = &spi->ctlr;
+ ctlr->dev = dev;
+
+ ctlr->setup = sifive_spi_setup;
+ ctlr->transfer = sifive_spi_transfer;
+ ctlr->mem_ops = &sifive_spi_mem_ops;
+
+ ctlr->bus_num = -1;
+
+ spi_controller_set_devdata(ctlr, spi);
+
+ sifive_spi_dt_probe(spi);
+
+ clkdev = clk_get(dev, NULL);
+ if (IS_ERR(clkdev))
+ return PTR_ERR(clkdev);
+
+ spi->freq = clk_get_rate(clkdev);
+
+ /* init the sifive spi hw */
+ sifive_spi_init_hw(spi);
+
+ return spi_register_master(ctlr);
+}
+
+static const struct of_device_id sifive_spi_ids[] = {
+ { .compatible = "sifive,spi0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sifive_spi_ids);
+
+static struct driver sifive_spi_driver = {
+ .name = "sifive_spi",
+ .probe = sifive_spi_probe,
+ .of_compatible = sifive_spi_ids,
+};
+coredevice_platform_driver(sifive_spi_driver);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d9311d4af5..c627d88954 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1,21 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2008 Sascha Hauer, Pengutronix
*
* Derived from Linux SPI Framework
*
* Copyright (C) 2005 David Brownell
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
*/
#include <common.h>
@@ -23,6 +12,7 @@
#include <spi/spi.h>
#include <xfuncs.h>
#include <malloc.h>
+#include <slice.h>
#include <errno.h>
#include <init.h>
#include <of.h>
@@ -40,6 +30,7 @@ struct boardinfo {
};
static LIST_HEAD(board_list);
+static LIST_HEAD(spi_controller_list);
/**
* spi_new_device - instantiate one new SPI device
@@ -82,7 +73,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctrl,
/* allocate a free id for this chip */
proxy->dev.id = DEVICE_ID_DYNAMIC;
proxy->dev.type_data = proxy;
- proxy->dev.device_node = chip->device_node;
+ proxy->dev.of_node = chip->device_node;
proxy->dev.parent = ctrl->dev;
proxy->master = proxy->controller = ctrl;
@@ -103,7 +94,11 @@ struct spi_device *spi_new_device(struct spi_controller *ctrl,
goto fail;
}
- register_device(&proxy->dev);
+ status = register_device(&proxy->dev);
+ if (status)
+ goto fail;
+
+ chip->device_node->dev = &proxy->dev;
return proxy;
fail:
@@ -115,9 +110,8 @@ EXPORT_SYMBOL(spi_new_device);
static void spi_of_register_slaves(struct spi_controller *ctrl)
{
struct device_node *n;
- struct spi_board_info chip;
struct property *reg;
- struct device_node *node = ctrl->dev->device_node;
+ struct device_node *node = ctrl->dev->of_node;
if (!IS_ENABLED(CONFIG_OFDEVICE))
return;
@@ -126,7 +120,14 @@ static void spi_of_register_slaves(struct spi_controller *ctrl)
return;
for_each_available_child_of_node(node, n) {
- memset(&chip, 0, sizeof(chip));
+ struct spi_board_info chip = {};
+
+ if (n->dev) {
+ dev_dbg(ctrl->dev, "skipping already registered %s\n",
+ dev_name(n->dev));
+ continue;
+ }
+
chip.name = xstrdup(n->name);
chip.bus_num = ctrl->bus_num;
/* Mode (clock phase/polarity/etc.) */
@@ -145,7 +146,18 @@ static void spi_of_register_slaves(struct spi_controller *ctrl)
continue;
chip.chip_select = of_read_number(reg->value, 1);
chip.device_node = n;
- spi_register_board_info(&chip, 1);
+ spi_new_device(ctrl, &chip);
+ }
+}
+
+static void spi_controller_rescan(struct device *dev)
+{
+ struct spi_controller *ctrl;
+
+ list_for_each_entry(ctrl, &spi_controller_list, list) {
+ if (ctrl->dev != dev)
+ continue;
+ spi_of_register_slaves(ctrl);
}
}
@@ -203,8 +215,6 @@ static void scan_boardinfo(struct spi_controller *ctrl)
}
}
-static LIST_HEAD(spi_controller_list);
-
static int spi_controller_check_ops(struct spi_controller *ctlr)
{
/*
@@ -260,14 +270,16 @@ int spi_register_controller(struct spi_controller *ctrl)
if (status)
return status;
+ slice_init(&ctrl->slice, dev_name(ctrl->dev));
+
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
if (ctrl->num_chipselect == 0)
return -EINVAL;
- if ((ctrl->bus_num < 0) && ctrl->dev->device_node)
- ctrl->bus_num = of_alias_get_id(ctrl->dev->device_node, "spi");
+ if ((ctrl->bus_num < 0) && ctrl->dev->of_node)
+ ctrl->bus_num = of_alias_get_id(ctrl->dev->of_node, "spi");
/* convention: dynamically assigned bus IDs count down from the max */
if (ctrl->bus_num < 0)
@@ -281,9 +293,12 @@ int spi_register_controller(struct spi_controller *ctrl)
scan_boardinfo(ctrl);
status = 0;
+ if (!ctrl->dev->rescan)
+ ctrl->dev->rescan = spi_controller_rescan;
+
return status;
}
-EXPORT_SYMBOL(spi_register_ctrl);
+EXPORT_SYMBOL(spi_register_controller);
struct spi_controller *spi_get_controller(int bus)
{
@@ -297,9 +312,62 @@ struct spi_controller *spi_get_controller(int bus)
return NULL;
}
+static int __spi_validate(struct spi_device *spi, struct spi_message *message)
+{
+ struct spi_controller *ctlr = spi->controller;
+ struct spi_transfer *xfer;
+ int w_size;
+
+ if (list_empty(&message->transfers))
+ return -EINVAL;
+
+ list_for_each_entry(xfer, &message->transfers, transfer_list) {
+ if (!xfer->bits_per_word)
+ xfer->bits_per_word = spi->bits_per_word;
+
+ if (!xfer->speed_hz)
+ xfer->speed_hz = spi->max_speed_hz;
+
+ if (ctlr->max_speed_hz && xfer->speed_hz > ctlr->max_speed_hz)
+ xfer->speed_hz = ctlr->max_speed_hz;
+
+ /*
+ * SPI transfer length should be multiple of SPI word size
+ * where SPI word size should be power-of-two multiple
+ */
+ if (xfer->bits_per_word <= 8)
+ w_size = 1;
+ else if (xfer->bits_per_word <= 16)
+ w_size = 2;
+ else
+ w_size = 4;
+
+ /* No partial transfers accepted */
+ if (xfer->len % w_size)
+ return -EINVAL;
+ }
+
+ message->status = -EINPROGRESS;
+
+ return 0;
+}
+
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
- return spi->controller->transfer(spi, message);
+ int status;
+ int ret;
+
+ status = __spi_validate(spi, message);
+ if (status != 0)
+ return status;
+
+ slice_acquire(&spi->controller->slice);
+
+ ret = spi->controller->transfer(spi, message);
+
+ slice_release(&spi->controller->slice);
+
+ return ret;
}
/**
@@ -344,22 +412,9 @@ int spi_write_then_read(struct spi_device *spi,
}
EXPORT_SYMBOL(spi_write_then_read);
-static int spi_probe(struct device_d *dev)
-{
- return dev->driver->probe(dev);
-}
-
-static void spi_remove(struct device_d *dev)
-{
- if (dev->driver->remove)
- dev->driver->remove(dev);
-}
-
struct bus_type spi_bus = {
.name = "spi",
.match = device_match_of_modalias,
- .probe = spi_probe,
- .remove = spi_remove,
};
static int spi_bus_init(void)
diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c
new file mode 100644
index 0000000000..9ef405a788
--- /dev/null
+++ b/drivers/spi/stm32_spi.c
@@ -0,0 +1,639 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * Driver for STMicroelectronics Serial peripheral interface (SPI)
+ */
+
+#include <common.h>
+#include <linux/clk.h>
+#include <driver.h>
+#include <init.h>
+#include <errno.h>
+#include <linux/reset.h>
+#include <linux/spi/spi-mem.h>
+#include <spi/spi.h>
+#include <linux/bitops.h>
+#include <clock.h>
+#include <gpio.h>
+#include <of_gpio.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+
+/* STM32 SPI registers */
+#define STM32_SPI_CR1 0x00
+#define STM32_SPI_CR2 0x04
+#define STM32_SPI_CFG1 0x08
+#define STM32_SPI_CFG2 0x0C
+#define STM32_SPI_SR 0x14
+#define STM32_SPI_IFCR 0x18
+#define STM32_SPI_TXDR 0x20
+#define STM32_SPI_RXDR 0x30
+#define STM32_SPI_I2SCFGR 0x50
+
+/* STM32_SPI_CR1 bit fields */
+#define SPI_CR1_SPE BIT(0)
+#define SPI_CR1_MASRX BIT(8)
+#define SPI_CR1_CSTART BIT(9)
+#define SPI_CR1_CSUSP BIT(10)
+#define SPI_CR1_HDDIR BIT(11)
+#define SPI_CR1_SSI BIT(12)
+
+/* STM32_SPI_CR2 bit fields */
+#define SPI_CR2_TSIZE GENMASK(15, 0)
+
+/* STM32_SPI_CFG1 bit fields */
+#define SPI_CFG1_DSIZE GENMASK(4, 0)
+#define SPI_CFG1_DSIZE_MIN 3
+#define SPI_CFG1_FTHLV_SHIFT 5
+#define SPI_CFG1_FTHLV GENMASK(8, 5)
+#define SPI_CFG1_MBR_SHIFT 28
+#define SPI_CFG1_MBR GENMASK(30, 28)
+#define SPI_CFG1_MBR_MIN 0
+#define SPI_CFG1_MBR_MAX FIELD_GET(SPI_CFG1_MBR, SPI_CFG1_MBR)
+
+/* STM32_SPI_CFG2 bit fields */
+#define SPI_CFG2_COMM_SHIFT 17
+#define SPI_CFG2_COMM GENMASK(18, 17)
+#define SPI_CFG2_MASTER BIT(22)
+#define SPI_CFG2_LSBFRST BIT(23)
+#define SPI_CFG2_CPHA BIT(24)
+#define SPI_CFG2_CPOL BIT(25)
+#define SPI_CFG2_SSM BIT(26)
+#define SPI_CFG2_AFCNTR BIT(31)
+
+/* STM32_SPI_SR bit fields */
+#define SPI_SR_RXP BIT(0)
+#define SPI_SR_TXP BIT(1)
+#define SPI_SR_EOT BIT(3)
+#define SPI_SR_TXTF BIT(4)
+#define SPI_SR_OVR BIT(6)
+#define SPI_SR_SUSP BIT(11)
+#define SPI_SR_RXPLVL_SHIFT 13
+#define SPI_SR_RXPLVL GENMASK(14, 13)
+#define SPI_SR_RXWNE BIT(15)
+
+/* STM32_SPI_IFCR bit fields */
+#define SPI_IFCR_ALL GENMASK(11, 3)
+
+/* STM32_SPI_I2SCFGR bit fields */
+#define SPI_I2SCFGR_I2SMOD BIT(0)
+
+/* SPI Master Baud Rate min/max divisor */
+#define STM32_MBR_DIV_MIN (2 << SPI_CFG1_MBR_MIN)
+#define STM32_MBR_DIV_MAX (2 << SPI_CFG1_MBR_MAX)
+
+/* SPI Communication mode */
+#define SPI_FULL_DUPLEX 0
+#define SPI_SIMPLEX_TX 1
+#define SPI_SIMPLEX_RX 2
+#define SPI_HALF_DUPLEX 3
+
+struct stm32_spi_priv {
+ struct spi_master master;
+ int *cs_gpios;
+ void __iomem *base;
+ struct clk *clk;
+ ulong bus_clk_rate;
+ unsigned int fifo_size;
+ unsigned int cur_bpw;
+ unsigned int cur_hz;
+ unsigned int cur_xferlen; /* current transfer length in bytes */
+ unsigned int tx_len; /* number of data to be written in bytes */
+ unsigned int rx_len; /* number of data to be read in bytes */
+ const void *tx_buf; /* data to be written, or NULL */
+ void *rx_buf; /* data to be read, or NULL */
+ u32 cur_mode;
+};
+
+static inline struct stm32_spi_priv *to_stm32_spi_priv(struct spi_master *master)
+{
+ return container_of(master, struct stm32_spi_priv, master);
+}
+
+static int stm32_spi_get_bpw_mask(struct stm32_spi_priv *priv)
+{
+ u32 cfg1, max_bpw;
+
+ /*
+ * The most significant bit at DSIZE bit field is reserved when the
+ * maximum data size of periperal instances is limited to 16-bit
+ */
+ setbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_DSIZE);
+
+ cfg1 = readl(priv->base + STM32_SPI_CFG1);
+ max_bpw = FIELD_GET(SPI_CFG1_DSIZE, cfg1) + 1;
+
+ dev_dbg(priv->master.dev, "%d-bit maximum data frame\n", max_bpw);
+
+ return SPI_BPW_RANGE_MASK(4, max_bpw);
+}
+
+static void stm32_spi_write_txfifo(struct stm32_spi_priv *priv)
+{
+ while ((priv->tx_len > 0) &&
+ (readl(priv->base + STM32_SPI_SR) & SPI_SR_TXP)) {
+ u32 offs = priv->cur_xferlen - priv->tx_len;
+
+ if (priv->tx_len >= sizeof(u32) &&
+ IS_ALIGNED((uintptr_t)(priv->tx_buf + offs), sizeof(u32))) {
+ const u32 *tx_buf32 = (const u32 *)(priv->tx_buf + offs);
+
+ writel(*tx_buf32, priv->base + STM32_SPI_TXDR);
+ priv->tx_len -= sizeof(u32);
+ } else if (priv->tx_len >= sizeof(u16) &&
+ IS_ALIGNED((uintptr_t)(priv->tx_buf + offs), sizeof(u16))) {
+ const u16 *tx_buf16 = (const u16 *)(priv->tx_buf + offs);
+
+ writew(*tx_buf16, priv->base + STM32_SPI_TXDR);
+ priv->tx_len -= sizeof(u16);
+ } else {
+ const u8 *tx_buf8 = (const u8 *)(priv->tx_buf + offs);
+
+ writeb(*tx_buf8, priv->base + STM32_SPI_TXDR);
+ priv->tx_len -= sizeof(u8);
+ }
+ }
+
+ dev_dbg(priv->master.dev, "%d bytes left\n", priv->tx_len);
+}
+
+static void stm32_spi_read_rxfifo(struct stm32_spi_priv *priv)
+{
+ u32 sr = readl(priv->base + STM32_SPI_SR);
+ u32 rxplvl = (sr & SPI_SR_RXPLVL) >> SPI_SR_RXPLVL_SHIFT;
+
+ while ((priv->rx_len > 0) &&
+ ((sr & SPI_SR_RXP) ||
+ ((sr & SPI_SR_EOT) && ((sr & SPI_SR_RXWNE) || (rxplvl > 0))))) {
+ u32 offs = priv->cur_xferlen - priv->rx_len;
+
+ if (IS_ALIGNED((uintptr_t)(priv->rx_buf + offs), sizeof(u32)) &&
+ (priv->rx_len >= sizeof(u32) || (sr & SPI_SR_RXWNE))) {
+ u32 *rx_buf32 = (u32 *)(priv->rx_buf + offs);
+
+ *rx_buf32 = readl(priv->base + STM32_SPI_RXDR);
+ priv->rx_len -= sizeof(u32);
+ } else if (IS_ALIGNED((uintptr_t)(priv->rx_buf + offs), sizeof(u16)) &&
+ (priv->rx_len >= sizeof(u16) ||
+ (!(sr & SPI_SR_RXWNE) &&
+ (rxplvl >= 2 || priv->cur_bpw > 8)))) {
+ u16 *rx_buf16 = (u16 *)(priv->rx_buf + offs);
+
+ *rx_buf16 = readw(priv->base + STM32_SPI_RXDR);
+ priv->rx_len -= sizeof(u16);
+ } else {
+ u8 *rx_buf8 = (u8 *)(priv->rx_buf + offs);
+
+ *rx_buf8 = readb(priv->base + STM32_SPI_RXDR);
+ priv->rx_len -= sizeof(u8);
+ }
+
+ sr = readl(priv->base + STM32_SPI_SR);
+ rxplvl = (sr & SPI_SR_RXPLVL) >> SPI_SR_RXPLVL_SHIFT;
+ }
+
+ dev_dbg(priv->master.dev, "%d bytes left\n", priv->rx_len);
+}
+
+static void stm32_spi_enable(struct stm32_spi_priv *priv)
+{
+ setbits_le32(priv->base + STM32_SPI_CR1, SPI_CR1_SPE);
+}
+
+static void stm32_spi_disable(struct stm32_spi_priv *priv)
+{
+ clrbits_le32(priv->base + STM32_SPI_CR1, SPI_CR1_SPE);
+}
+
+static void stm32_spi_stopxfer(struct stm32_spi_priv *priv)
+{
+ struct device *dev = priv->master.dev;
+ u32 cr1, sr;
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ cr1 = readl(priv->base + STM32_SPI_CR1);
+
+ if (!(cr1 & SPI_CR1_SPE))
+ return;
+
+ /* Wait on EOT or suspend the flow */
+ ret = readl_poll_timeout(priv->base + STM32_SPI_SR, sr,
+ !(sr & SPI_SR_EOT), USEC_PER_SEC);
+ if (ret < 0) {
+ if (cr1 & SPI_CR1_CSTART) {
+ writel(cr1 | SPI_CR1_CSUSP, priv->base + STM32_SPI_CR1);
+ if (readl_poll_timeout(priv->base + STM32_SPI_SR,
+ sr, !(sr & SPI_SR_SUSP),
+ 100000) < 0)
+ dev_err(dev, "Suspend request timeout\n");
+ }
+ }
+
+ /* clear status flags */
+ setbits_le32(priv->base + STM32_SPI_IFCR, SPI_IFCR_ALL);
+}
+
+static void stm32_spi_set_cs(struct spi_device *spi, bool en)
+{
+ struct stm32_spi_priv *priv = to_stm32_spi_priv(spi->master);
+ int gpio = priv->cs_gpios[spi->chip_select];
+ int ret = -EINVAL;
+
+ dev_dbg(priv->master.dev, "cs=%d en=%d\n", gpio, en);
+
+ if (gpio_is_valid(gpio))
+ ret = gpio_direction_output(gpio, (spi->mode & SPI_CS_HIGH) ? en : !en);
+
+ if (ret)
+ dev_warn(priv->master.dev, "couldn't toggle cs#%u\n", spi->chip_select);
+}
+
+static void stm32_spi_set_mode(struct stm32_spi_priv *priv, unsigned mode)
+{
+ u32 cfg2_clrb = 0, cfg2_setb = 0;
+
+ dev_dbg(priv->master.dev, "mode=%d\n", mode);
+
+ if (mode & SPI_CPOL)
+ cfg2_setb |= SPI_CFG2_CPOL;
+ else
+ cfg2_clrb |= SPI_CFG2_CPOL;
+
+ if (mode & SPI_CPHA)
+ cfg2_setb |= SPI_CFG2_CPHA;
+ else
+ cfg2_clrb |= SPI_CFG2_CPHA;
+
+ if (mode & SPI_LSB_FIRST)
+ cfg2_setb |= SPI_CFG2_LSBFRST;
+ else
+ cfg2_clrb |= SPI_CFG2_LSBFRST;
+
+ if (cfg2_clrb || cfg2_setb)
+ clrsetbits_le32(priv->base + STM32_SPI_CFG2,
+ cfg2_clrb, cfg2_setb);
+}
+
+static void stm32_spi_set_fthlv(struct stm32_spi_priv *priv, u32 xfer_len)
+{
+ u32 fthlv, packet, bpw;
+
+ /* data packet should not exceed 1/2 of fifo space */
+ packet = clamp(xfer_len, 1U, priv->fifo_size / 2);
+
+ /* align packet size with data registers access */
+ bpw = DIV_ROUND_UP(priv->cur_bpw, 8);
+ fthlv = DIV_ROUND_UP(packet, bpw);
+
+ clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_FTHLV,
+ (fthlv - 1) << SPI_CFG1_FTHLV_SHIFT);
+}
+
+static int stm32_spi_set_speed(struct stm32_spi_priv *priv, uint hz)
+{
+ u32 mbrdiv;
+ long div;
+
+ dev_dbg(priv->master.dev, "hz=%d\n", hz);
+
+ if (priv->cur_hz == hz)
+ return 0;
+
+ div = DIV_ROUND_UP(priv->bus_clk_rate, hz);
+
+ if (div < STM32_MBR_DIV_MIN || div > STM32_MBR_DIV_MAX)
+ return -EINVAL;
+
+ /* Determine the first power of 2 greater than or equal to div */
+ if (div & (div - 1))
+ mbrdiv = fls(div);
+ else
+ mbrdiv = fls(div) - 1;
+
+ if (!mbrdiv)
+ return -EINVAL;
+
+ clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_MBR,
+ (mbrdiv - 1) << SPI_CFG1_MBR_SHIFT);
+
+ priv->cur_hz = hz;
+
+ return 0;
+}
+
+static int stm32_spi_setup(struct spi_device *spi)
+{
+ struct stm32_spi_priv *priv = to_stm32_spi_priv(spi->master);
+ int ret;
+
+ stm32_spi_set_cs(spi, false);
+ stm32_spi_enable(priv);
+
+ stm32_spi_set_mode(priv, spi->mode);
+
+ ret = stm32_spi_set_speed(priv, spi->max_speed_hz);
+ if (ret)
+ goto out;
+
+ priv->cur_bpw = spi->bits_per_word;
+ clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_DSIZE,
+ priv->cur_bpw - 1);
+
+ dev_dbg(priv->master.dev, "%s mode 0x%08x bits_per_word: %d speed: %d\n",
+ __func__, spi->mode, spi->bits_per_word,
+ spi->max_speed_hz);
+out:
+ stm32_spi_disable(priv);
+ return ret;
+}
+
+static int stm32_spi_transfer_one(struct stm32_spi_priv *priv,
+ struct spi_transfer *t)
+{
+ struct device *dev = priv->master.dev;
+ u32 sr;
+ u32 ifcr = 0;
+ u32 mode;
+ int xfer_status = 0;
+ int nb_words;
+
+ if (t->bits_per_word <= 8)
+ nb_words = t->len;
+ else if (t->bits_per_word <= 16)
+ nb_words = DIV_ROUND_UP(t->len * 8, 16);
+ else
+ nb_words = DIV_ROUND_UP(t->len * 8, 32);
+
+ if (nb_words <= SPI_CR2_TSIZE)
+ writel(nb_words, priv->base + STM32_SPI_CR2);
+ else
+ return -EMSGSIZE;
+
+ priv->tx_buf = t->tx_buf;
+ priv->rx_buf = t->rx_buf;
+ priv->tx_len = priv->tx_buf ? t->len : 0;
+ priv->rx_len = priv->rx_buf ? t->len : 0;
+
+ mode = SPI_FULL_DUPLEX;
+ if (!priv->tx_buf)
+ mode = SPI_SIMPLEX_RX;
+ else if (!priv->rx_buf)
+ mode = SPI_SIMPLEX_TX;
+
+ if (priv->cur_xferlen != t->len || priv->cur_mode != mode ||
+ priv->cur_bpw != t->bits_per_word) {
+ priv->cur_mode = mode;
+ priv->cur_xferlen = t->len;
+ priv->cur_bpw = t->bits_per_word;
+
+ /* Disable the SPI hardware to unlock CFG1/CFG2 registers */
+ stm32_spi_disable(priv);
+
+ clrsetbits_le32(priv->base + STM32_SPI_CFG2, SPI_CFG2_COMM,
+ mode << SPI_CFG2_COMM_SHIFT);
+
+ stm32_spi_set_fthlv(priv, t->len);
+
+ clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_DSIZE,
+ priv->cur_bpw - 1);
+
+ /* Enable the SPI hardware */
+ stm32_spi_enable(priv);
+ }
+
+ dev_dbg(dev, "priv->tx_len=%d priv->rx_len=%d\n",
+ priv->tx_len, priv->rx_len);
+
+ /* Be sure to have data in fifo before starting data transfer */
+ if (priv->tx_buf)
+ stm32_spi_write_txfifo(priv);
+
+ setbits_le32(priv->base + STM32_SPI_CR1, SPI_CR1_CSTART);
+
+ while (1) {
+ sr = readl(priv->base + STM32_SPI_SR);
+
+ if (sr & SPI_SR_OVR) {
+ dev_err(dev, "Overrun: RX data lost\n");
+ xfer_status = -EIO;
+ break;
+ }
+
+ if (sr & SPI_SR_SUSP) {
+ dev_warn(dev, "System too slow is limiting data throughput\n");
+
+ if (priv->rx_buf && priv->rx_len > 0)
+ stm32_spi_read_rxfifo(priv);
+
+ ifcr |= SPI_SR_SUSP;
+ }
+
+ if (sr & SPI_SR_TXTF)
+ ifcr |= SPI_SR_TXTF;
+
+ if (sr & SPI_SR_TXP)
+ if (priv->tx_buf && priv->tx_len > 0)
+ stm32_spi_write_txfifo(priv);
+
+ if (sr & SPI_SR_RXP)
+ if (priv->rx_buf && priv->rx_len > 0)
+ stm32_spi_read_rxfifo(priv);
+
+ if (sr & SPI_SR_EOT) {
+ if (priv->rx_buf && priv->rx_len > 0)
+ stm32_spi_read_rxfifo(priv);
+ break;
+ }
+
+ writel(ifcr, priv->base + STM32_SPI_IFCR);
+ }
+
+ /* clear status flags */
+ setbits_le32(priv->base + STM32_SPI_IFCR, SPI_IFCR_ALL);
+ stm32_spi_stopxfer(priv);
+
+ return xfer_status;
+}
+
+static int stm32_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
+{
+ struct stm32_spi_priv *priv = to_stm32_spi_priv(spi->master);
+ struct spi_transfer *t;
+ unsigned int cs_change;
+ const int nsecs = 50;
+ int ret = 0;
+
+ stm32_spi_enable(priv);
+
+ stm32_spi_set_cs(spi, true);
+
+ cs_change = 0;
+
+ mesg->actual_length = 0;
+
+ list_for_each_entry(t, &mesg->transfers, transfer_list) {
+ if (cs_change) {
+ ndelay(nsecs);
+ stm32_spi_set_cs(spi, false);
+ ndelay(nsecs);
+ stm32_spi_set_cs(spi, true);
+ }
+
+ cs_change = t->cs_change;
+
+ ret = stm32_spi_transfer_one(priv, t);
+ if (ret)
+ goto out;
+
+ mesg->actual_length += t->len;
+
+ if (cs_change)
+ stm32_spi_set_cs(spi, true);
+ }
+
+ if (!cs_change)
+ stm32_spi_set_cs(spi, false);
+
+out:
+ stm32_spi_disable(priv);
+ return ret;
+}
+
+static int stm32_spi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+ if (op->data.nbytes > SPI_CR2_TSIZE)
+ op->data.nbytes = SPI_CR2_TSIZE;
+
+ return 0;
+}
+
+static int stm32_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+ return -ENOTSUPP;
+}
+
+static const struct spi_controller_mem_ops stm32_spi_mem_ops = {
+ .adjust_op_size = stm32_spi_adjust_op_size,
+ .exec_op = stm32_spi_exec_op,
+};
+
+static int stm32_spi_get_fifo_size(struct stm32_spi_priv *priv)
+{
+ u32 count = 0;
+
+ stm32_spi_enable(priv);
+
+ while (readl(priv->base + STM32_SPI_SR) & SPI_SR_TXP)
+ writeb(++count, priv->base + STM32_SPI_TXDR);
+
+ stm32_spi_disable(priv);
+
+ dev_dbg(priv->master.dev, "%d x 8-bit fifo size\n", count);
+
+ return count;
+}
+
+static void stm32_spi_dt_probe(struct stm32_spi_priv *priv)
+{
+ struct device_node *node = priv->master.dev->of_node;
+ int i;
+
+ priv->master.num_chipselect = of_gpio_count_csgpios(node);
+ priv->cs_gpios = xzalloc(sizeof(u32) * priv->master.num_chipselect);
+
+ for (i = 0; i < priv->master.num_chipselect; i++)
+ priv->cs_gpios[i] = of_get_named_gpio(node, "cs-gpios", i);
+}
+
+static int stm32_spi_probe(struct device *dev)
+{
+ struct resource *iores;
+ struct spi_master *master;
+ struct stm32_spi_priv *priv;
+ int ret;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ priv = dev->priv = xzalloc(sizeof(*priv));
+
+ priv->base = IOMEM(iores->start);
+
+ master = &priv->master;
+ master->dev = dev;
+
+ master->setup = stm32_spi_setup;
+ master->transfer = stm32_spi_transfer;
+ master->mem_ops = &stm32_spi_mem_ops;
+
+ master->bus_num = -1;
+ stm32_spi_dt_probe(priv);
+
+ priv->clk = clk_get(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ ret = clk_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ priv->bus_clk_rate = clk_get_rate(priv->clk);
+
+ ret = device_reset_us(dev, 2);
+ if (ret)
+ return ret;
+
+ master->bits_per_word_mask = stm32_spi_get_bpw_mask(priv);
+ priv->fifo_size = stm32_spi_get_fifo_size(priv);
+
+ priv->cur_mode = SPI_FULL_DUPLEX;
+ priv->cur_xferlen = 0;
+
+ /* Ensure I2SMOD bit is kept cleared */
+ clrbits_le32(priv->base + STM32_SPI_I2SCFGR, SPI_I2SCFGR_I2SMOD);
+
+ /*
+ * - SS input value high
+ * - transmitter half duplex direction
+ * - automatic communication suspend when RX-Fifo is full
+ */
+ setbits_le32(priv->base + STM32_SPI_CR1,
+ SPI_CR1_SSI | SPI_CR1_HDDIR | SPI_CR1_MASRX);
+
+ /*
+ * - Set the master mode (default Motorola mode)
+ * - Consider 1 master/n slaves configuration and
+ * SS input value is determined by the SSI bit
+ * - keep control of all associated GPIOs
+ */
+ setbits_le32(priv->base + STM32_SPI_CFG2,
+ SPI_CFG2_MASTER | SPI_CFG2_SSM | SPI_CFG2_AFCNTR);
+
+ return spi_register_master(master);
+}
+
+static void stm32_spi_remove(struct device *dev)
+{
+ struct stm32_spi_priv *priv = dev->priv;
+
+ stm32_spi_stopxfer(priv);
+ stm32_spi_disable(priv);
+};
+
+static const struct of_device_id stm32_spi_ids[] = {
+ { .compatible = "st,stm32h7-spi", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, stm32_spi_ids);
+
+static struct driver stm32_spi_driver = {
+ .name = "stm32_spi",
+ .probe = stm32_spi_probe,
+ .remove = stm32_spi_remove,
+ .of_compatible = stm32_spi_ids,
+};
+coredevice_platform_driver(stm32_spi_driver);
diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c
new file mode 100644
index 0000000000..3da245feb7
--- /dev/null
+++ b/drivers/spi/zynq_qspi.c
@@ -0,0 +1,613 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 Xilinx, Inc.
+ *
+ * Author: Naga Sureshkumar Relli <nagasure@xilinx.com>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/spi/spi-mem.h>
+#include <spi/spi.h>
+
+/* Register offset definitions */
+#define ZYNQ_QSPI_CONFIG_OFFSET 0x00 /* Configuration Register, RW */
+#define ZYNQ_QSPI_STATUS_OFFSET 0x04 /* Interrupt Status Register, RO */
+#define ZYNQ_QSPI_IEN_OFFSET 0x08 /* Interrupt Enable Register, WO */
+#define ZYNQ_QSPI_IDIS_OFFSET 0x0C /* Interrupt Disable Reg, WO */
+#define ZYNQ_QSPI_IMASK_OFFSET 0x10 /* Interrupt Enabled Mask Reg,RO */
+#define ZYNQ_QSPI_ENABLE_OFFSET 0x14 /* Enable/Disable Register, RW */
+#define ZYNQ_QSPI_DELAY_OFFSET 0x18 /* Delay Register, RW */
+#define ZYNQ_QSPI_TXD_00_00_OFFSET 0x1C /* Transmit 4-byte inst, WO */
+#define ZYNQ_QSPI_TXD_00_01_OFFSET 0x80 /* Transmit 1-byte inst, WO */
+#define ZYNQ_QSPI_TXD_00_10_OFFSET 0x84 /* Transmit 2-byte inst, WO */
+#define ZYNQ_QSPI_TXD_00_11_OFFSET 0x88 /* Transmit 3-byte inst, WO */
+#define ZYNQ_QSPI_RXD_OFFSET 0x20 /* Data Receive Register, RO */
+#define ZYNQ_QSPI_SIC_OFFSET 0x24 /* Slave Idle Count Register, RW */
+#define ZYNQ_QSPI_TX_THRESH_OFFSET 0x28 /* TX FIFO Watermark Reg, RW */
+#define ZYNQ_QSPI_RX_THRESH_OFFSET 0x2C /* RX FIFO Watermark Reg, RW */
+#define ZYNQ_QSPI_GPIO_OFFSET 0x30 /* GPIO Register, RW */
+#define ZYNQ_QSPI_LINEAR_CFG_OFFSET 0xA0 /* Linear Adapter Config Ref, RW */
+#define ZYNQ_QSPI_MOD_ID_OFFSET 0xFC /* Module ID Register, RO */
+
+/*
+ * QSPI Configuration Register bit Masks
+ *
+ * This register contains various control bits that effect the operation
+ * of the QSPI controller
+ */
+#define ZYNQ_QSPI_CONFIG_IFMODE_MASK BIT(31) /* Flash Memory Interface */
+#define ZYNQ_QSPI_CONFIG_MANSRT_MASK BIT(16) /* Manual TX Start */
+#define ZYNQ_QSPI_CONFIG_MANSRTEN_MASK BIT(15) /* Enable Manual TX Mode */
+#define ZYNQ_QSPI_CONFIG_SSFORCE_MASK BIT(14) /* Manual Chip Select */
+#define ZYNQ_QSPI_CONFIG_BDRATE_MASK GENMASK(5, 3) /* Baud Rate Mask */
+#define ZYNQ_QSPI_CONFIG_CPHA_MASK BIT(2) /* Clock Phase Control */
+#define ZYNQ_QSPI_CONFIG_CPOL_MASK BIT(1) /* Clock Polarity Control */
+#define ZYNQ_QSPI_CONFIG_SSCTRL_MASK BIT(10) /* Slave Select Mask */
+#define ZYNQ_QSPI_CONFIG_FWIDTH_MASK GENMASK(7, 6) /* FIFO width */
+#define ZYNQ_QSPI_CONFIG_MSTREN_MASK BIT(0) /* Master Mode */
+
+/*
+ * QSPI Configuration Register - Baud rate and slave select
+ *
+ * These are the values used in the calculation of baud rate divisor and
+ * setting the slave select.
+ */
+#define ZYNQ_QSPI_BAUD_DIV_MAX GENMASK(2, 0) /* Baud rate maximum */
+#define ZYNQ_QSPI_BAUD_DIV_SHIFT 3 /* Baud rate divisor shift in CR */
+#define ZYNQ_QSPI_SS_SHIFT 10 /* Slave Select field shift in CR */
+
+/*
+ * QSPI Interrupt Registers bit Masks
+ *
+ * All the four interrupt registers (Status/Mask/Enable/Disable) have the same
+ * bit definitions.
+ */
+#define ZYNQ_QSPI_IXR_RX_OVERFLOW_MASK BIT(0) /* QSPI RX FIFO Overflow */
+#define ZYNQ_QSPI_IXR_TXNFULL_MASK BIT(2) /* QSPI TX FIFO Overflow */
+#define ZYNQ_QSPI_IXR_TXFULL_MASK BIT(3) /* QSPI TX FIFO is full */
+#define ZYNQ_QSPI_IXR_RXNEMTY_MASK BIT(4) /* QSPI RX FIFO Not Empty */
+#define ZYNQ_QSPI_IXR_RXF_FULL_MASK BIT(5) /* QSPI RX FIFO is full */
+#define ZYNQ_QSPI_IXR_TXF_UNDRFLOW_MASK BIT(6) /* QSPI TX FIFO Underflow */
+#define ZYNQ_QSPI_IXR_ALL_MASK (ZYNQ_QSPI_IXR_RX_OVERFLOW_MASK | \
+ ZYNQ_QSPI_IXR_TXNFULL_MASK | \
+ ZYNQ_QSPI_IXR_TXFULL_MASK | \
+ ZYNQ_QSPI_IXR_RXNEMTY_MASK | \
+ ZYNQ_QSPI_IXR_RXF_FULL_MASK | \
+ ZYNQ_QSPI_IXR_TXF_UNDRFLOW_MASK)
+#define ZYNQ_QSPI_IXR_RXTX_MASK (ZYNQ_QSPI_IXR_TXNFULL_MASK | \
+ ZYNQ_QSPI_IXR_RXNEMTY_MASK)
+
+/*
+ * QSPI Enable Register bit Masks
+ *
+ * This register is used to enable or disable the QSPI controller
+ */
+#define ZYNQ_QSPI_ENABLE_ENABLE_MASK BIT(0) /* QSPI Enable Bit Mask */
+
+/*
+ * QSPI Linear Configuration Register
+ *
+ * It is named Linear Configuration but it controls other modes when not in
+ * linear mode also.
+ */
+#define ZYNQ_QSPI_LCFG_TWO_MEM_MASK BIT(30) /* LQSPI Two memories Mask */
+#define ZYNQ_QSPI_LCFG_SEP_BUS_MASK BIT(29) /* LQSPI Separate bus Mask */
+#define ZYNQ_QSPI_LCFG_U_PAGE_MASK BIT(28) /* LQSPI Upper Page Mask */
+
+#define ZYNQ_QSPI_LCFG_DUMMY_SHIFT 8
+
+#define ZYNQ_QSPI_FAST_READ_QOUT_CODE 0x6B /* read instruction code */
+#define ZYNQ_QSPI_FIFO_DEPTH 63 /* FIFO depth in words */
+#define ZYNQ_QSPI_RX_THRESHOLD 32 /* Rx FIFO threshold level */
+#define ZYNQ_QSPI_TX_THRESHOLD 1 /* Tx FIFO threshold level */
+
+/*
+ * The modebits configurable by the driver to make the SPI support different
+ * data formats
+ */
+#define ZYNQ_QSPI_MODEBITS (SPI_CPOL | SPI_CPHA)
+
+/**
+ * struct zynq_qspi - Defines qspi driver instance
+ * @regs: Virtual address of the QSPI controller registers
+ * @refclk: Pointer to the peripheral clock
+ * @pclk: Pointer to the APB clock
+ * @irq: IRQ number
+ * @txbuf: Pointer to the TX buffer
+ * @rxbuf: Pointer to the RX buffer
+ * @tx_bytes: Number of bytes left to transfer
+ * @rx_bytes: Number of bytes left to receive
+ * @data_completion: completion structure
+ */
+struct zynq_qspi {
+ struct spi_controller ctlr;
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *refclk;
+ struct clk *pclk;
+ int tx_bytes;
+ int rx_bytes;
+ u8 *txbuf;
+ u8 *rxbuf;
+};
+
+/*
+ * Inline functions for the QSPI controller read/write
+ */
+static inline u32 zynq_qspi_read(struct zynq_qspi *xqspi, u32 offset)
+{
+ return readl(xqspi->regs + offset);
+}
+
+static inline void zynq_qspi_write(struct zynq_qspi *xqspi, u32 offset, u32 val)
+{
+ writel(val, xqspi->regs + offset);
+}
+
+/**
+ * zynq_qspi_init_hw - Initialize the hardware
+ * @xqspi: Pointer to the zynq_qspi structure
+ *
+ * The default settings of the QSPI controller's configurable parameters on
+ * reset are
+ * - Master mode
+ * - Baud rate divisor is set to 2
+ * - Tx threshold set to 1l Rx threshold set to 32
+ * - Flash memory interface mode enabled
+ * - Size of the word to be transferred as 8 bit
+ * This function performs the following actions
+ * - Disable and clear all the interrupts
+ * - Enable manual slave select
+ * - Enable manual start
+ * - Deselect all the chip select lines
+ * - Set the size of the word to be transferred as 32 bit
+ * - Set the little endian mode of TX FIFO and
+ * - Enable the QSPI controller
+ */
+static void zynq_qspi_init_hw(struct zynq_qspi *xqspi)
+{
+ u32 config_reg;
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0);
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_IDIS_OFFSET, ZYNQ_QSPI_IXR_ALL_MASK);
+
+ /* Disable linear mode as the boot loader may have used it */
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET, 0);
+
+ /* Clear the RX FIFO */
+ while (zynq_qspi_read(xqspi, ZYNQ_QSPI_STATUS_OFFSET) &
+ ZYNQ_QSPI_IXR_RXNEMTY_MASK)
+ zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_STATUS_OFFSET, ZYNQ_QSPI_IXR_ALL_MASK);
+ config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
+ config_reg &= ~(ZYNQ_QSPI_CONFIG_MSTREN_MASK |
+ ZYNQ_QSPI_CONFIG_CPOL_MASK |
+ ZYNQ_QSPI_CONFIG_CPHA_MASK |
+ ZYNQ_QSPI_CONFIG_BDRATE_MASK |
+ ZYNQ_QSPI_CONFIG_SSFORCE_MASK |
+ ZYNQ_QSPI_CONFIG_MANSRTEN_MASK |
+ ZYNQ_QSPI_CONFIG_MANSRT_MASK);
+ config_reg |= (ZYNQ_QSPI_CONFIG_MSTREN_MASK |
+ ZYNQ_QSPI_CONFIG_SSFORCE_MASK |
+ ZYNQ_QSPI_CONFIG_FWIDTH_MASK |
+ ZYNQ_QSPI_CONFIG_IFMODE_MASK);
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_RX_THRESH_OFFSET,
+ ZYNQ_QSPI_RX_THRESHOLD);
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_TX_THRESH_OFFSET,
+ ZYNQ_QSPI_TX_THRESHOLD);
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET,
+ ZYNQ_QSPI_ENABLE_ENABLE_MASK);
+}
+
+/**
+ * zynq_qspi_rxfifo_op - Read 1..4 bytes from RxFIFO to RX buffer
+ * @xqspi: Pointer to the zynq_qspi structure
+ * @size: Number of bytes to be read (1..4)
+ */
+static void zynq_qspi_rxfifo_op(struct zynq_qspi *xqspi, unsigned int size)
+{
+ u32 data;
+
+ data = zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
+
+ if (xqspi->rxbuf) {
+ memcpy(xqspi->rxbuf, ((u8 *)&data) + 4 - size, size);
+ xqspi->rxbuf += size;
+ }
+
+ xqspi->rx_bytes -= size;
+ if (xqspi->rx_bytes < 0)
+ xqspi->rx_bytes = 0;
+}
+
+/**
+ * zynq_qspi_txfifo_op - Write 1..4 bytes from TX buffer to TxFIFO
+ * @xqspi: Pointer to the zynq_qspi structure
+ * @size: Number of bytes to be written (1..4)
+ */
+static void zynq_qspi_txfifo_op(struct zynq_qspi *xqspi, unsigned int size)
+{
+ static const unsigned int offset[4] = {
+ ZYNQ_QSPI_TXD_00_01_OFFSET, ZYNQ_QSPI_TXD_00_10_OFFSET,
+ ZYNQ_QSPI_TXD_00_11_OFFSET, ZYNQ_QSPI_TXD_00_00_OFFSET };
+ u32 data;
+
+ if (xqspi->txbuf) {
+ data = 0xffffffff;
+ memcpy(&data, xqspi->txbuf, size);
+ xqspi->txbuf += size;
+ } else {
+ data = 0;
+ }
+
+ xqspi->tx_bytes -= size;
+ zynq_qspi_write(xqspi, offset[size - 1], data);
+}
+
+/**
+ * zynq_qspi_chipselect - Select or deselect the chip select line
+ * @spi: Pointer to the spi_device structure
+ * @assert: 1 for select or 0 for deselect the chip select line
+ */
+static void zynq_qspi_chipselect(struct spi_device *spi, bool assert)
+{
+ struct spi_controller *ctrl = spi->master;
+ struct zynq_qspi *xqspi = spi_controller_get_devdata(ctrl);
+ u32 config_reg;
+
+ config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
+ if (assert) {
+ /* Select the slave */
+ config_reg &= ~ZYNQ_QSPI_CONFIG_SSCTRL_MASK;
+ config_reg |= (((~(BIT(spi->chip_select))) <<
+ ZYNQ_QSPI_SS_SHIFT) &
+ ZYNQ_QSPI_CONFIG_SSCTRL_MASK);
+ } else {
+ config_reg |= ZYNQ_QSPI_CONFIG_SSCTRL_MASK;
+ }
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
+}
+
+/**
+ * zynq_qspi_config_op - Configure QSPI controller for specified transfer
+ * @xqspi: Pointer to the zynq_qspi structure
+ * @qspi: Pointer to the spi_device structure
+ *
+ * Sets the operational mode of QSPI controller for the next QSPI transfer and
+ * sets the requested clock frequency.
+ *
+ * Return: 0 on success and -EINVAL on invalid input parameter
+ *
+ * Note: If the requested frequency is not an exact match with what can be
+ * obtained using the prescalar value, the driver sets the clock frequency which
+ * is lower than the requested frequency (maximum lower) for the transfer. If
+ * the requested frequency is higher or lower than that is supported by the QSPI
+ * controller the driver will set the highest or lowest frequency supported by
+ * controller.
+ */
+static int zynq_qspi_config_op(struct zynq_qspi *xqspi, struct spi_device *spi)
+{
+ u32 config_reg, baud_rate_val = 0;
+
+ /*
+ * Set the clock frequency
+ * The baud rate divisor is not a direct mapping to the value written
+ * into the configuration register (config_reg[5:3])
+ * i.e. 000 - divide by 2
+ * 001 - divide by 4
+ * ----------------
+ * 111 - divide by 256
+ */
+ while ((baud_rate_val < ZYNQ_QSPI_BAUD_DIV_MAX) &&
+ (clk_get_rate(xqspi->refclk) / (2 << baud_rate_val)) >
+ spi->max_speed_hz)
+ baud_rate_val++;
+
+ config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
+
+ /* Set the QSPI clock phase and clock polarity */
+ config_reg &= (~ZYNQ_QSPI_CONFIG_CPHA_MASK) &
+ (~ZYNQ_QSPI_CONFIG_CPOL_MASK);
+ if (spi->mode & SPI_CPHA)
+ config_reg |= ZYNQ_QSPI_CONFIG_CPHA_MASK;
+ if (spi->mode & SPI_CPOL)
+ config_reg |= ZYNQ_QSPI_CONFIG_CPOL_MASK;
+
+ config_reg &= ~ZYNQ_QSPI_CONFIG_BDRATE_MASK;
+ config_reg |= (baud_rate_val << ZYNQ_QSPI_BAUD_DIV_SHIFT);
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
+
+ return 0;
+}
+
+static int zynq_qspi_setup_op(struct spi_device *spi)
+{
+ struct spi_controller *ctrl = spi->master;
+ struct zynq_qspi *xqspi = spi_controller_get_devdata(ctrl);
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET,
+ ZYNQ_QSPI_ENABLE_ENABLE_MASK);
+
+ return 0;
+}
+
+/**
+ * zynq_qspi_write_op - Fills the TX FIFO with as many bytes as possible
+ * @xqspi: Pointer to the zynq_qspi structure
+ * @txcount: Maximum number of words to write
+ * @txempty: Indicates that TxFIFO is empty
+ */
+static void zynq_qspi_write_op(struct zynq_qspi *xqspi, int txcount,
+ bool txempty)
+{
+ int count, len, k;
+
+ len = xqspi->tx_bytes;
+ if (len && len < sizeof(u32)) {
+ /*
+ * We must empty the TxFIFO between accesses to TXD0,
+ * TXD1, TXD2, TXD3.
+ */
+ if (txempty)
+ zynq_qspi_txfifo_op(xqspi, len);
+
+ return;
+ }
+
+ count = len / sizeof(u32);
+ if (count > txcount)
+ count = txcount;
+
+ if (xqspi->txbuf) {
+ u32 *buf = (u32 *)xqspi->txbuf;
+ for (k = 0; k < count; k++, buf++)
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_TXD_00_00_OFFSET, *buf);
+ xqspi->txbuf += count * sizeof(u32);
+ } else {
+ for (k = 0; k < count; k++)
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_TXD_00_00_OFFSET, 0);
+ }
+
+ xqspi->tx_bytes -= count * sizeof(u32);
+}
+
+/**
+ * zynq_qspi_read_op - Drains the RX FIFO by as many bytes as possible
+ * @xqspi: Pointer to the zynq_qspi structure
+ * @rxcount: Maximum number of words to read
+ */
+static void zynq_qspi_read_op(struct zynq_qspi *xqspi, int rxcount)
+{
+ int count, len, k;
+
+ len = xqspi->rx_bytes - xqspi->tx_bytes;
+ count = len / sizeof(u32);
+ if (count > rxcount)
+ count = rxcount;
+ if (xqspi->rxbuf) {
+ u32 *buf = (u32 *)xqspi->rxbuf;
+ for (k = 0; k < count; k++, buf++)
+ *buf = zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
+ xqspi->rxbuf += count * sizeof(u32);
+ } else {
+ for (k = 0; k < count; k++)
+ zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
+ }
+ xqspi->rx_bytes -= count * sizeof(u32);
+ len -= count * sizeof(u32);
+
+ if (len && len < 4 && count < rxcount)
+ zynq_qspi_rxfifo_op(xqspi, len);
+}
+
+static int zynq_qspi_poll_irq(struct zynq_qspi *xqspi)
+{
+ u32 intr_status;
+ bool txempty;
+ int ret;
+
+ for (;;) {
+ ret = readl_poll_timeout(xqspi->regs + ZYNQ_QSPI_STATUS_OFFSET,
+ intr_status, intr_status,
+ 100 * USEC_PER_MSEC);
+ if (ret)
+ return ret;
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_STATUS_OFFSET, intr_status);
+
+ if ((intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK) ||
+ (intr_status & ZYNQ_QSPI_IXR_RXNEMTY_MASK)) {
+ /*
+ * This bit is set when Tx FIFO has < THRESHOLD entries.
+ * We have the THRESHOLD value set to 1,
+ * so this bit indicates Tx FIFO is empty.
+ */
+ txempty = !!(intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK);
+ /* Read out the data from the RX FIFO */
+ zynq_qspi_read_op(xqspi, ZYNQ_QSPI_RX_THRESHOLD);
+ if (xqspi->tx_bytes) {
+ /* There is more data to send */
+ zynq_qspi_write_op(xqspi, ZYNQ_QSPI_RX_THRESHOLD,
+ txempty);
+ } else if (!xqspi->rx_bytes){
+ /* No more RX or TX bytes -> transfer done */
+ return 0;
+ }
+ }
+ }
+}
+
+/**
+ * zynq_qspi_exec_mem_op() - Initiates the QSPI transfer
+ * @mem: the SPI memory
+ * @op: the memory operation to execute
+ *
+ * Executes a memory operation.
+ *
+ * This function first selects the chip and starts the memory operation.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct zynq_qspi *xqspi = spi_controller_get_devdata(mem->spi->master);
+ int ret, i;
+ u8 *tmpbuf;
+
+ dev_dbg(xqspi->dev, "cmd:%#x mode:%d.%d.%d.%d\n",
+ op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
+ op->dummy.buswidth, op->data.buswidth);
+
+ zynq_qspi_chipselect(mem->spi, true);
+ zynq_qspi_config_op(xqspi, mem->spi);
+
+ if (op->cmd.opcode) {
+ xqspi->txbuf = (u8 *)&op->cmd.opcode;
+ xqspi->rxbuf = NULL;
+ xqspi->tx_bytes = sizeof(op->cmd.opcode);
+ xqspi->rx_bytes = sizeof(op->cmd.opcode);
+ zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
+ ret = zynq_qspi_poll_irq(xqspi);
+ if (ret)
+ return ret;
+ }
+
+ if (op->addr.nbytes) {
+ for (i = 0; i < op->addr.nbytes; i++) {
+ xqspi->txbuf[i] = op->addr.val >>
+ (8 * (op->addr.nbytes - i - 1));
+ }
+
+ xqspi->rxbuf = NULL;
+ xqspi->tx_bytes = op->addr.nbytes;
+ xqspi->rx_bytes = op->addr.nbytes;
+ zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
+ ret = zynq_qspi_poll_irq(xqspi);
+ if (ret)
+ return ret;
+ }
+
+ if (op->dummy.nbytes) {
+ tmpbuf = kzalloc(op->dummy.nbytes, GFP_KERNEL);
+ memset(tmpbuf, 0xff, op->dummy.nbytes);
+ xqspi->txbuf = tmpbuf;
+ xqspi->rxbuf = NULL;
+ xqspi->tx_bytes = op->dummy.nbytes;
+ xqspi->rx_bytes = op->dummy.nbytes;
+ zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
+ ret = zynq_qspi_poll_irq(xqspi);
+ kfree(tmpbuf);
+ if (ret)
+ return ret;
+ }
+
+ if (op->data.nbytes) {
+ if (op->data.dir == SPI_MEM_DATA_OUT) {
+ xqspi->txbuf = (u8 *)op->data.buf.out;
+ xqspi->tx_bytes = op->data.nbytes;
+ xqspi->rxbuf = NULL;
+ xqspi->rx_bytes = op->data.nbytes;
+ } else {
+ xqspi->txbuf = NULL;
+ xqspi->rxbuf = (u8 *)op->data.buf.in;
+ xqspi->rx_bytes = op->data.nbytes;
+ xqspi->tx_bytes = op->data.nbytes;
+ }
+
+ zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
+ ret = zynq_qspi_poll_irq(xqspi);
+ if (ret)
+ return ret;
+ }
+
+ zynq_qspi_chipselect(mem->spi, false);
+
+ return 0;
+}
+
+static const struct spi_controller_mem_ops zynq_qspi_mem_ops = {
+ .exec_op = zynq_qspi_exec_mem_op,
+};
+
+static int zynq_qspi_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct spi_controller *ctlr;
+ struct zynq_qspi *xqspi;
+ struct resource *iores;
+ u32 num_cs;
+ int ret;
+
+ xqspi = xzalloc(sizeof(*xqspi));
+
+ ctlr = &xqspi->ctlr;
+ xqspi->dev = dev;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ xqspi->regs = IOMEM(iores->start);
+
+ xqspi->pclk = clk_get(dev, "pclk");
+ if (IS_ERR(xqspi->pclk)) {
+ dev_err(dev, "pclk clock not found.\n");
+ return PTR_ERR(xqspi->pclk);
+ }
+
+ xqspi->refclk = clk_get(dev, "ref_clk");
+ if (IS_ERR(xqspi->refclk)) {
+ dev_err(dev, "ref_clk clock not found.\n");
+ return PTR_ERR(xqspi->refclk);
+ }
+
+ ret = clk_enable(xqspi->pclk);
+ if (ret) {
+ dev_err(dev, "Unable to enable APB clock.\n");
+ return ret;
+ }
+
+ ret = clk_enable(xqspi->refclk);
+ if (ret) {
+ dev_err(dev, "Unable to enable device clock.\n");
+ return ret;
+ }
+
+ /* QSPI controller initializations */
+ zynq_qspi_init_hw(xqspi);
+
+ if (of_property_read_u32(np, "num-cs", &num_cs))
+ ctlr->num_chipselect = 1;
+ else
+ ctlr->num_chipselect = num_cs;
+
+ ctlr->dev = dev;
+ ctlr->mem_ops = &zynq_qspi_mem_ops;
+ ctlr->setup = zynq_qspi_setup_op;
+
+ spi_controller_set_devdata(ctlr, xqspi);
+
+ return spi_register_controller(ctlr);
+}
+
+static const struct of_device_id zynq_qspi_of_match[] = {
+ { .compatible = "xlnx,zynq-qspi-1.0", },
+ { /* end of table */ }
+};
+MODULE_DEVICE_TABLE(of, zynq_qspi_of_match);
+
+static struct driver zynq_qspi_driver = {
+ .name = "zynq-qspi",
+ .probe = zynq_qspi_probe,
+ .of_compatible = DRV_OF_COMPAT(zynq_qspi_of_match),
+};
+device_platform_driver(zynq_qspi_driver);