diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 50 | ||||
-rw-r--r-- | drivers/spi/Makefile | 8 | ||||
-rw-r--r-- | drivers/spi/altera_spi.c | 247 | ||||
-rw-r--r-- | drivers/spi/ath79_spi.c | 30 | ||||
-rw-r--r-- | drivers/spi/atmel-quadspi.c | 19 | ||||
-rw-r--r-- | drivers/spi/atmel_spi.c | 30 | ||||
-rw-r--r-- | drivers/spi/atmel_spi.h | 5 | ||||
-rw-r--r-- | drivers/spi/dspi_spi.c | 26 | ||||
-rw-r--r-- | drivers/spi/gpio_spi.c | 29 | ||||
-rw-r--r-- | drivers/spi/imx_spi.c | 32 | ||||
-rw-r--r-- | drivers/spi/litex_spiflash.c | 242 | ||||
-rw-r--r-- | drivers/spi/mvebu_spi.c | 18 | ||||
-rw-r--r-- | drivers/spi/mxs_spi.c | 16 | ||||
-rw-r--r-- | drivers/spi/omap3_spi.c | 27 | ||||
-rw-r--r-- | drivers/spi/omap3_spi.h | 15 | ||||
-rw-r--r-- | drivers/spi/spi-bitbang-txrx.h | 1 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-dspi.c | 656 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-qspi.c | 13 | ||||
-rw-r--r-- | drivers/spi/spi-mem.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-nxp-fspi.c | 1064 | ||||
-rw-r--r-- | drivers/spi/spi-sifive.c | 522 | ||||
-rw-r--r-- | drivers/spi/spi.c | 129 | ||||
-rw-r--r-- | drivers/spi/stm32_spi.c | 639 | ||||
-rw-r--r-- | drivers/spi/zynq_qspi.c | 613 |
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); |