diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 28 | ||||
-rw-r--r-- | drivers/spi/Makefile | 5 | ||||
-rw-r--r-- | drivers/spi/altera_spi.c | 247 | ||||
-rw-r--r-- | drivers/spi/ath79_spi.c | 23 | ||||
-rw-r--r-- | drivers/spi/atmel-quadspi.c | 16 | ||||
-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 | 9 | ||||
-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 | 24 | ||||
-rw-r--r-- | drivers/spi/omap3_spi.h | 12 | ||||
-rw-r--r-- | drivers/spi/spi-bitbang-txrx.h | 1 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-dspi.c | 17 | ||||
-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 | 75 | ||||
-rw-r--r-- | drivers/spi/stm32_spi.c | 639 | ||||
-rw-r--r-- | drivers/spi/zynq_qspi.c | 15 |
24 files changed, 2401 insertions, 467 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index e92cf675ab..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 @@ -106,6 +103,29 @@ config SPI_ZYNQ_QSPI 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 ac95ffc1db..68a8c4e675 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -1,3 +1,4 @@ +# 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 @@ -7,10 +8,12 @@ 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 2a4e32a4cc..41a31ae922 100644 --- a/drivers/spi/ath79_spi.c +++ b/drivers/spi/ath79_spi.c @@ -1,18 +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. - * - * 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> @@ -136,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", @@ -230,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; @@ -248,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; @@ -279,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; @@ -294,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 af2191726f..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,14 +21,12 @@ #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> @@ -159,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; }; @@ -416,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; @@ -529,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 index e1fc5a4d3f..58ce6ad5f5 100644 --- a/drivers/spi/litex_spiflash.c +++ b/drivers/spi/litex_spiflash.c @@ -93,7 +93,7 @@ static inline void litex_spiflash_spi_chipselect(struct litex_spiflash_spi *sc, static int litex_spiflash_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", @@ -184,7 +184,7 @@ static void litex_spiflash_spi_disable(struct litex_spiflash_spi *sp) litex_spiflash_spi_wr(sp, 0, SPIFLASH_BITBANG_EN); } -static int litex_spiflash_spi_probe(struct device_d *dev) +static int litex_spiflash_spi_probe(struct device *dev) { struct resource *iores; struct spi_master *master; @@ -216,7 +216,7 @@ static int litex_spiflash_spi_probe(struct device_d *dev) return 0; } -static void litex_spiflash_spi_remove(struct device_d *dev) +static void litex_spiflash_spi_remove(struct device *dev) { struct litex_spiflash_spi *sp = dev->priv; @@ -231,8 +231,9 @@ static __maybe_unused struct of_device_id litex_spiflash_spi_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, litex_spiflash_spi_dt_ids); -static struct driver_d litex_spiflash_spi_driver = { +static struct driver litex_spiflash_spi_driver = { .name = "litex-spiflash", .probe = litex_spiflash_spi_probe, .remove = litex_spiflash_spi_remove, 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 ff9eccc37d..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,17 +13,6 @@ * Copyright (C) 2005, 2006 Nokia Corporation * * Modified by Ruslan Araslanov <ruslan.araslanov@vitecmm.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> @@ -342,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; @@ -431,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[] = { { @@ -444,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 8b90920157..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,17 +8,6 @@ * Copyright (C) 2005, 2006 Nokia Corporation * * Modified by Ruslan Araslanov <ruslan.araslanov@vitecmm.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. - * */ #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 index a7ceb854e9..f032e2673e 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later // // Copyright 2013 Freescale Semiconductor, Inc. // @@ -10,7 +10,7 @@ #include <errno.h> #include <init.h> #include <io.h> -#include <regmap.h> +#include <linux/regmap.h> #include <spi/spi.h> #include <linux/clk.h> #include <linux/math64.h> @@ -132,7 +132,7 @@ static const struct fsl_dspi_devtype_data ls2085a_data = { struct fsl_dspi { struct spi_controller ctlr; - struct device_d *dev; + struct device *dev; struct regmap *regmap; struct regmap *regmap_pushr; @@ -462,10 +462,10 @@ static int dspi_setup(struct spi_device *spi) return -ENOMEM; } - of_property_read_u32(spi->dev.device_node, "fsl,spi-cs-sck-delay", + of_property_read_u32(spi->dev.of_node, "fsl,spi-cs-sck-delay", &cs_sck_delay); - of_property_read_u32(spi->dev.device_node, "fsl,spi-sck-cs-delay", + of_property_read_u32(spi->dev.of_node, "fsl,spi-sck-cs-delay", &sck_cs_delay); chip->void_write_data = 0; @@ -515,6 +515,7 @@ static const struct of_device_id fsl_dspi_dt_ids[] = { { .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, @@ -553,9 +554,9 @@ static void dspi_init(struct fsl_dspi *dspi) SPI_CTARE_FMSZE(0) | SPI_CTARE_DTCP(1)); } -static int dspi_probe(struct device_d *dev) +static int dspi_probe(struct device *dev) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; const struct regmap_config *regmap_config; struct spi_master *master; int ret, cs_num, bus_num = -1; @@ -647,7 +648,7 @@ out_ctlr_put: return ret; } -static struct driver_d fsl_dspi_driver = { +static struct driver fsl_dspi_driver = { .name = "fsl-dspi", .probe = dspi_probe, .of_compatible = DRV_OF_COMPAT(fsl_dspi_dt_ids), 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 d1d3bdcc41..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; @@ -119,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; @@ -130,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.) */ @@ -149,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); } } @@ -207,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) { /* @@ -264,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) @@ -285,6 +293,9 @@ 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_controller); @@ -344,12 +355,19 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) int spi_sync(struct spi_device *spi, struct spi_message *message) { int status; + int ret; status = __spi_validate(spi, message); if (status != 0) return status; - return spi->controller->transfer(spi, message); + slice_acquire(&spi->controller->slice); + + ret = spi->controller->transfer(spi, message); + + slice_release(&spi->controller->slice); + + return ret; } /** @@ -394,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 index d01e4a8e6c..3da245feb7 100644 --- a/drivers/spi/zynq_qspi.c +++ b/drivers/spi/zynq_qspi.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2019 Xilinx, Inc. * @@ -125,7 +125,7 @@ */ struct zynq_qspi { struct spi_controller ctlr; - struct device_d *dev; + struct device *dev; void __iomem *regs; struct clk *refclk; struct clk *pclk; @@ -539,9 +539,9 @@ 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_d *dev) +static int zynq_qspi_probe(struct device *dev) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct spi_controller *ctlr; struct zynq_qspi *xqspi; struct resource *iores; @@ -559,13 +559,13 @@ static int zynq_qspi_probe(struct device_d *dev) xqspi->regs = IOMEM(iores->start); xqspi->pclk = clk_get(dev, "pclk"); - if (IS_ERR_OR_NULL(xqspi->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_OR_NULL(xqspi->refclk)) { + if (IS_ERR(xqspi->refclk)) { dev_err(dev, "ref_clk clock not found.\n"); return PTR_ERR(xqspi->refclk); } @@ -603,8 +603,9 @@ 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_d zynq_qspi_driver = { +static struct driver zynq_qspi_driver = { .name = "zynq-qspi", .probe = zynq_qspi_probe, .of_compatible = DRV_OF_COMPAT(zynq_qspi_of_match), |