diff options
Diffstat (limited to 'drivers/spi')
| -rw-r--r-- | drivers/spi/Kconfig | 34 | ||||
| -rw-r--r-- | drivers/spi/Makefile | 5 | ||||
| -rw-r--r-- | drivers/spi/spi-atmel.c | 30 | ||||
| -rw-r--r-- | drivers/spi/spi-bcm63xx-hsspi.c | 1 | ||||
| -rw-r--r-- | drivers/spi/spi-bcm63xx.c | 4 | ||||
| -rw-r--r-- | drivers/spi/spi-davinci.c | 9 | ||||
| -rw-r--r-- | drivers/spi/spi-fsl-dspi.c | 3 | ||||
| -rw-r--r-- | drivers/spi/spi-imx.c | 92 | ||||
| -rw-r--r-- | drivers/spi/spi-loopback-test.c | 14 | ||||
| -rw-r--r-- | drivers/spi/spi-meson-spicc.c | 619 | ||||
| -rw-r--r-- | drivers/spi/spi-mt65xx.c | 61 | ||||
| -rw-r--r-- | drivers/spi/spi-slave-system-control.c | 154 | ||||
| -rw-r--r-- | drivers/spi/spi-slave-time.c | 129 | ||||
| -rw-r--r-- | drivers/spi/spi.c | 1220 |
14 files changed, 1752 insertions, 623 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 1761c9004fc1..fd1b4fdb72a4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -393,6 +393,13 @@ config SPI_FSL_ESPI From MPC8536, 85xx platform uses the controller, and all P10xx, P20xx, P30xx,P40xx, P50xx uses this controller. +config SPI_MESON_SPICC + tristate "Amlogic Meson SPICC controller" + depends on ARCH_MESON || COMPILE_TEST + help + This enables master mode support for the SPICC (SPI communication + controller) available in Amlogic Meson SoCs. + config SPI_MESON_SPIFC tristate "Amlogic Meson SPIFC controller" depends on ARCH_MESON || COMPILE_TEST @@ -457,6 +464,7 @@ config SPI_OMAP24XX config SPI_TI_QSPI tristate "DRA7xxx QSPI controller support" + depends on HAS_DMA depends on ARCH_OMAP2PLUS || COMPILE_TEST help QSPI master controller for DRA7xxx used for flash devices. @@ -784,6 +792,30 @@ config SPI_TLE62X0 endif # SPI_MASTER -# (slave support would go here) +# +# SLAVE side ... listening to other SPI masters +# + +config SPI_SLAVE + bool "SPI slave protocol handlers" + help + If your system has a slave-capable SPI controller, you can enable + slave protocol handlers. + +if SPI_SLAVE + +config SPI_SLAVE_TIME + tristate "SPI slave handler reporting boot up time" + help + SPI slave handler responding with the time of reception of the last + SPI message. + +config SPI_SLAVE_SYSTEM_CONTROL + tristate "SPI slave handler controlling system state" + help + SPI slave handler to allow remote control of system reboot, power + off, halt, and suspend. + +endif # SPI_SLAVE endif # SPI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index b375a7a89216..31dccfbb335e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o obj-$(CONFIG_SPI_JCORE) += spi-jcore.o obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o +obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o @@ -105,3 +106,7 @@ obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o obj-$(CONFIG_SPI_XLP) += spi-xlp.o obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o + +# SPI slave protocol handlers +obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o +obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 1eb83c9613d5..f95da364c283 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -269,6 +269,7 @@ struct atmel_spi_caps { bool is_spi2; bool has_wdrbt; bool has_dma_support; + bool has_pdc_support; }; /* @@ -1422,11 +1423,31 @@ static void atmel_get_caps(struct atmel_spi *as) unsigned int version; version = atmel_get_version(as); - dev_info(&as->pdev->dev, "version: 0x%x\n", version); as->caps.is_spi2 = version > 0x121; as->caps.has_wdrbt = version >= 0x210; +#ifdef CONFIG_SOC_SAM_V4_V5 + /* + * Atmel SoCs based on ARM9 (SAM9x) cores should not use spi_map_buf() + * since this later function tries to map buffers with dma_map_sg() + * even if they have not been allocated inside DMA-safe areas. + * On SoCs based on Cortex A5 (SAMA5Dx), it works anyway because for + * those ARM cores, the data cache follows the PIPT model. + * Also the L2 cache controller of SAMA5D2 uses the PIPT model too. + * In case of PIPT caches, there cannot be cache aliases. + * However on ARM9 cores, the data cache follows the VIVT model, hence + * the cache aliases issue can occur when buffers are allocated from + * DMA-unsafe areas, by vmalloc() for instance, where cache coherency is + * not taken into account or at least not handled completely (cache + * lines of aliases are not invalidated). + * This is not a theorical issue: it was reproduced when trying to mount + * a UBI file-system on a at91sam9g35ek board. + */ + as->caps.has_dma_support = false; +#else as->caps.has_dma_support = version >= 0x212; +#endif + as->caps.has_pdc_support = version < 0x212; } /*-------------------------------------------------------------------------*/ @@ -1567,7 +1588,7 @@ static int atmel_spi_probe(struct platform_device *pdev) } else if (ret == -EPROBE_DEFER) { return ret; } - } else { + } else if (as->caps.has_pdc_support) { as->use_pdc = true; } @@ -1609,8 +1630,9 @@ static int atmel_spi_probe(struct platform_device *pdev) goto out_free_dma; /* go! */ - dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n", - (unsigned long)regs->start, irq); + dev_info(&pdev->dev, "Atmel SPI Controller version 0x%x at 0x%08lx (irq %d)\n", + atmel_get_version(as), (unsigned long)regs->start, + irq); return 0; diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index 5514cd02e93a..4da2d4a524ca 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -484,6 +484,7 @@ static const struct of_device_id bcm63xx_hsspi_of_match[] = { { .compatible = "brcm,bcm6328-hsspi", }, { }, }; +MODULE_DEVICE_TABLE(of, bcm63xx_hsspi_of_match); static struct platform_driver bcm63xx_hsspi_driver = { .driver = { diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 247f71b02235..84c7356ce5b4 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -147,7 +147,7 @@ struct bcm63xx_spi { /* Platform data */ const unsigned long *reg_offsets; - unsigned fifo_size; + unsigned int fifo_size; unsigned int msg_type_shift; unsigned int msg_ctl_width; @@ -191,7 +191,7 @@ static inline void bcm_spi_writew(struct bcm63xx_spi *bs, #endif } -static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { +static const unsigned int bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { { 20000000, SPI_CLK_20MHZ }, { 12500000, SPI_CLK_12_50MHZ }, { 6250000, SPI_CLK_6_250MHZ }, diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 595acdcfc7d0..6ddb6ef1fda4 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -873,9 +873,8 @@ static int spi_davinci_get_pdata(struct platform_device *pdev, return 0; } #else -static struct davinci_spi_platform_data - *spi_davinci_get_pdata(struct platform_device *pdev, - struct davinci_spi *dspi) +static int spi_davinci_get_pdata(struct platform_device *pdev, + struct davinci_spi *dspi) { return -ENODEV; } @@ -965,7 +964,9 @@ static int davinci_spi_probe(struct platform_device *pdev) ret = -ENODEV; goto free_master; } - clk_prepare_enable(dspi->clk); + ret = clk_prepare_enable(dspi->clk); + if (ret) + goto free_master; master->dev.of_node = pdev->dev.of_node; master->bus_num = pdev->id; diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 15201645bdc4..d89127f4a46d 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1032,7 +1032,8 @@ static int dspi_probe(struct platform_device *pdev) goto out_master_put; if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE) { - if (dspi_request_dma(dspi, res->start)) { + ret = dspi_request_dma(dspi, res->start); + if (ret < 0) { dev_err(&pdev->dev, "can't get dma channels\n"); goto out_clk_put; } diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index b402530a7a9a..f9698b7aeb3b 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -56,10 +56,6 @@ /* The maximum bytes that a sdma BD can transfer.*/ #define MAX_SDMA_BD_BYTES (1 << 15) -struct spi_imx_config { - unsigned int speed_hz; - unsigned int bpw; -}; enum spi_imx_devtype { IMX1_CSPI, @@ -74,7 +70,7 @@ struct spi_imx_data; struct spi_imx_devtype_data { void (*intctrl)(struct spi_imx_data *, int); - int (*config)(struct spi_device *, struct spi_imx_config *); + int (*config)(struct spi_device *); void (*trigger)(struct spi_imx_data *); int (*rx_available)(struct spi_imx_data *); void (*reset)(struct spi_imx_data *); @@ -94,7 +90,8 @@ struct spi_imx_data { unsigned long spi_clk; unsigned int spi_bus_clk; - unsigned int bytes_per_word; + unsigned int speed_hz; + unsigned int bits_per_word; unsigned int spi_drctl; unsigned int count; @@ -203,34 +200,27 @@ out: return i; } -static int spi_imx_bytes_per_word(const int bpw) +static int spi_imx_bytes_per_word(const int bits_per_word) { - return DIV_ROUND_UP(bpw, BITS_PER_BYTE); + return DIV_ROUND_UP(bits_per_word, BITS_PER_BYTE); } static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *transfer) { struct spi_imx_data *spi_imx = spi_master_get_devdata(master); - unsigned int bpw, i; + unsigned int bytes_per_word, i; if (!master->dma_rx) return false; - if (!transfer) - return false; - - bpw = transfer->bits_per_word; - if (!bpw) - bpw = spi->bits_per_word; - - bpw = spi_imx_bytes_per_word(bpw); + bytes_per_word = spi_imx_bytes_per_word(transfer->bits_per_word); - if (bpw != 1 && bpw != 2 && bpw != 4) + if (bytes_per_word != 1 && bytes_per_word != 2 && bytes_per_word != 4) return false; for (i = spi_imx_get_fifosize(spi_imx) / 2; i > 0; i--) { - if (!(transfer->len % (i * bpw))) + if (!(transfer->len % (i * bytes_per_word))) break; } @@ -340,12 +330,11 @@ static void mx51_ecspi_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MX51_ECSPI_CTRL); } -static int mx51_ecspi_config(struct spi_device *spi, - struct spi_imx_config *config) +static int mx51_ecspi_config(struct spi_device *spi) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); u32 ctrl = MX51_ECSPI_CTRL_ENABLE; - u32 clk = config->speed_hz, delay, reg; + u32 clk = spi_imx->speed_hz, delay, reg; u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG); /* @@ -364,13 +353,13 @@ static int mx51_ecspi_config(struct spi_device *spi, ctrl |= MX51_ECSPI_CTRL_DRCTL(spi_imx->spi_drctl); /* set clock speed */ - ctrl |= mx51_ecspi_clkdiv(spi_imx, config->speed_hz, &clk); + ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->speed_hz, &clk); spi_imx->spi_bus_clk = clk; /* set chip select to use */ ctrl |= MX51_ECSPI_CTRL_CS(spi->chip_select); - ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET; + ctrl |= (spi_imx->bits_per_word - 1) << MX51_ECSPI_CTRL_BL_OFFSET; cfg |= MX51_ECSPI_CONFIG_SBBCTRL(spi->chip_select); @@ -501,21 +490,21 @@ static void mx31_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int mx31_config(struct spi_device *spi, struct spi_imx_config *config) +static int mx31_config(struct spi_device *spi) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; unsigned int clk; - reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz, &clk) << + reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, spi_imx->speed_hz, &clk) << MX31_CSPICTRL_DR_SHIFT; spi_imx->spi_bus_clk = clk; if (is_imx35_cspi(spi_imx)) { - reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT; + reg |= (spi_imx->bits_per_word - 1) << MX35_CSPICTRL_BL_SHIFT; reg |= MX31_CSPICTRL_SSCTL; } else { - reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT; + reg |= (spi_imx->bits_per_word - 1) << MX31_CSPICTRL_BC_SHIFT; } if (spi->mode & SPI_CPHA) @@ -597,18 +586,18 @@ static void mx21_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int mx21_config(struct spi_device *spi, struct spi_imx_config *config) +static int mx21_config(struct spi_device *spi) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX21_CSPICTRL_ENABLE | MX21_CSPICTRL_MASTER; unsigned int max = is_imx27_cspi(spi_imx) ? 16 : 18; unsigned int clk; - reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, config->speed_hz, max, &clk) + reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, spi_imx->speed_hz, max, &clk) << MX21_CSPICTRL_DR_SHIFT; spi_imx->spi_bus_clk = clk; - reg |= config->bpw - 1; + reg |= spi_imx->bits_per_word - 1; if (spi->mode & SPI_CPHA) reg |= MX21_CSPICTRL_PHA; @@ -666,17 +655,17 @@ static void mx1_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int mx1_config(struct spi_device *spi, struct spi_imx_config *config) +static int mx1_config(struct spi_device *spi) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER; unsigned int clk; - reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz, &clk) << + reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, spi_imx->speed_hz, &clk) << MX1_CSPICTRL_DR_SHIFT; spi_imx->spi_bus_clk = clk; - reg |= config->bpw - 1; + reg |= spi_imx->bits_per_word - 1; if (spi->mode & SPI_CPHA) reg |= MX1_CSPICTRL_PHA; @@ -841,15 +830,14 @@ static irqreturn_t spi_imx_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int spi_imx_dma_configure(struct spi_master *master, - int bytes_per_word) +static int spi_imx_dma_configure(struct spi_master *master) { int ret; enum dma_slave_buswidth buswidth; struct dma_slave_config rx = {}, tx = {}; struct spi_imx_data *spi_imx = spi_master_get_devdata(master); - switch (bytes_per_word) { + switch (spi_imx_bytes_per_word(spi_imx->bits_per_word)) { case 4: buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; break; @@ -883,8 +871,6 @@ static int spi_imx_dma_configure(struct spi_master *master, return ret; } - spi_imx->bytes_per_word = bytes_per_word; - return 0; } @@ -892,22 +878,19 @@ static int spi_imx_setupxfer(struct spi_device *spi, struct spi_transfer *t) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); - struct spi_imx_config config; int ret; - config.bpw = t ? t->bits_per_word : spi->bits_per_word; - config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; + if (!t) + return 0; - if (!config.speed_hz) - config.speed_hz = spi->max_speed_hz; - if (!config.bpw) - config.bpw = spi->bits_per_word; + spi_imx->bits_per_word = t->bits_per_word; + spi_imx->speed_hz = t->speed_hz; /* Initialize the functions for transfer */ - if (config.bpw <= 8) { + if (spi_imx->bits_per_word <= 8) { spi_imx->rx = spi_imx_buf_rx_u8; spi_imx->tx = spi_imx_buf_tx_u8; - } else if (config.bpw <= 16) { + } else if (spi_imx->bits_per_word <= 16) { spi_imx->rx = spi_imx_buf_rx_u16; spi_imx->tx = spi_imx_buf_tx_u16; } else { @@ -921,13 +904,12 @@ static int spi_imx_setupxfer(struct spi_device *spi, spi_imx->usedma = 0; if (spi_imx->usedma) { - ret = spi_imx_dma_configure(spi->master, - spi_imx_bytes_per_word(config.bpw)); + ret = spi_imx_dma_configure(spi->master); if (ret) return ret; } - spi_imx->devtype_data->config(spi, &config); + spi_imx->devtype_data->config(spi); return 0; } @@ -976,8 +958,6 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, goto err; } - spi_imx_dma_configure(master, 1); - init_completion(&spi_imx->dma_rx_completion); init_completion(&spi_imx->dma_tx_completion); master->can_dma = spi_imx_can_dma; @@ -1189,15 +1169,15 @@ static int spi_imx_probe(struct platform_device *pdev) } master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data)); + if (!master) + return -ENOMEM; + ret = of_property_read_u32(np, "fsl,spi-rdy-drctl", &spi_drctl); if ((ret < 0) || (spi_drctl >= 0x3)) { /* '11' is reserved */ spi_drctl = 0; } - if (!master) - return -ENOMEM; - platform_set_drvdata(pdev, master); master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index f4875f177df0..3459965004f8 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -894,7 +894,7 @@ int spi_test_execute_msg(struct spi_device *spi, struct spi_test *test, test->elapsed_time = ktime_to_ns(ktime_sub(ktime_get(), start)); if (ret == -ETIMEDOUT) { dev_info(&spi->dev, - "spi-message timed out - reruning...\n"); + "spi-message timed out - rerunning...\n"); /* rerun after a few explicit schedules */ for (i = 0; i < 16; i++) schedule(); @@ -1021,10 +1021,9 @@ int spi_test_run_tests(struct spi_device *spi, rx = vmalloc(SPI_TEST_MAX_SIZE_PLUS); else rx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL); - if (!rx) { - ret = -ENOMEM; - goto out; - } + if (!rx) + return -ENOMEM; + if (use_vmalloc) tx = vmalloc(SPI_TEST_MAX_SIZE_PLUS); @@ -1032,7 +1031,7 @@ int spi_test_run_tests(struct spi_device *spi, tx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL); if (!tx) { ret = -ENOMEM; - goto out; + goto err_tx; } /* now run the individual tests in the table */ @@ -1057,8 +1056,9 @@ int spi_test_run_tests(struct spi_device *spi, } out: - kvfree(rx); kvfree(tx); +err_tx: + kvfree(rx); return ret; } EXPORT_SYMBOL_GPL(spi_test_run_tests); diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c new file mode 100644 index 000000000000..7f8429635502 --- /dev/null +++ b/drivers/spi/spi-meson-spicc.c @@ -0,0 +1,619 @@ +/* + * Driver for Amlogic Meson SPI communication controller (SPICC) + * + * Copyright (C) BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/reset.h> +#include <linux/gpio.h> + +/* + * The Meson SPICC controller could support DMA based transfers, but is not + * implemented by the vendor code, and while having the registers documentation + * it has never worked on the GXL Hardware. + * The PIO mode is the only mode implemented, and due to badly designed HW : + * - all transfers are cutted in 16 words burst because the FIFO hangs on + * TX underflow, and there is no TX "Half-Empty" interrupt, so we go by + * FIFO max size chunk only + * - CS management is dumb, and goes UP between every burst, so is really a + * "Data Valid" signal than a Chip Select, GPIO link should be used instead + * to have a CS go down over the full transfer + */ + +#define SPICC_MAX_FREQ 30000000 +#define SPICC_MAX_BURST 128 + +/* Register Map */ +#define SPICC_RXDATA 0x00 + +#define SPICC_TXDATA 0x04 + +#define SPICC_CONREG 0x08 +#define SPICC_ENABLE BIT(0) +#define SPICC_MODE_MASTER BIT(1) +#define SPICC_XCH BIT(2) +#define SPICC_SMC BIT(3) +#define SPICC_POL BIT(4) +#define SPICC_PHA BIT(5) +#define SPICC_SSCTL BIT(6) +#define SPICC_SSPOL BIT(7) +#define SPICC_DRCTL_MASK GENMASK(9, 8) +#define SPICC_DRCTL_IGNORE 0 +#define SPICC_DRCTL_FALLING 1 +#define SPICC_DRCTL_LOWLEVEL 2 +#define SPICC_CS_MASK GENMASK(13, 12) +#define SPICC_DATARATE_MASK GENMASK(18, 16) +#define SPICC_DATARATE_DIV4 0 +#define SPICC_DATARATE_DIV8 1 +#define SPICC_DATARATE_DIV16 2 +#define SPICC_DATARATE_DIV32 3 +#define SPICC_BITLENGTH_MASK GENMASK(24, 19) +#define SPICC_BURSTLENGTH_MASK GENMASK(31, 25) + +#define SPICC_INTREG 0x0c +#define SPICC_TE_EN BIT(0) /* TX FIFO Empty Interrupt */ +#define SPICC_TH_EN BIT(1) /* TX FIFO Half-Full Interrupt */ +#define SPICC_TF_EN BIT(2) /* TX FIFO Full Interrupt */ +#define SPICC_RR_EN BIT(3) /* RX FIFO Ready Interrupt */ +#define SPICC_RH_EN BIT(4) /* RX FIFO Half-Full Interrupt */ +#define SPICC_RF_EN BIT(5) /* RX FIFO Full Interrupt */ +#define SPICC_RO_EN BIT(6) /* RX FIFO Overflow Interrupt */ +#define SPICC_TC_EN BIT(7) /* Transfert Complete Interrupt */ + +#define SPICC_DMAREG 0x10 +#define SPICC_DMA_ENABLE BIT(0) +#define SPICC_TXFIFO_THRESHOLD_MASK GENMASK(5, 1) +#define SPICC_RXFIFO_THRESHOLD_MASK GENMASK(10, 6) +#define SPICC_READ_BURST_MASK GENMASK(14, 11) +#define SPICC_WRITE_BURST_MASK GENMASK(18, 15) +#define SPICC_DMA_URGENT BIT(19) +#define SPICC_DMA_THREADID_MASK GENMASK(25, 20) +#define SPICC_DMA_BURSTNUM_MASK GENMASK(31, 26) + +#define SPICC_STATREG 0x14 +#define SPICC_TE BIT(0) /* TX FIFO Empty Interrupt */ +#define SPICC_TH BIT(1) /* TX FIFO Half-Full Interrupt */ +#define SPICC_TF BIT(2) /* TX FIFO Full Interrupt */ +#define SPICC_RR BIT(3) /* RX FIFO Ready Interrupt */ +#define SPICC_RH BIT(4) /* RX FIFO Half-Full Interrupt */ +#define SPICC_RF BIT(5) /* RX FIFO Full Interrupt */ +#define SPICC_RO BIT(6) /* RX FIFO Overflow Interrupt */ +#define SPICC_TC BIT(7) /* Transfert Complete Interrupt */ + +#define SPICC_PERIODREG 0x18 +#define SPICC_PERIOD GENMASK(14, 0) /* Wait cycles */ + +#define SPICC_TESTREG 0x1c +#define SPICC_TXCNT_MASK GENMASK(4, 0) /* TX FIFO Counter */ +#define SPICC_RXCNT_MASK GENMASK(9, 5) /* RX FIFO Counter */ +#define SPICC_SMSTATUS_MASK GENMASK(12, 10) /* State Machine Status */ +#define SPICC_LBC_RO BIT(13) /* Loop Back Control Read-Only */ +#define SPICC_LBC_W1 BIT(14) /* Loop Back Control Write-Only */ +#define SPICC_SWAP_RO BIT(14) /* RX FIFO Data Swap Read-Only */ +#define SPICC_SWAP_W1 BIT(15) /* RX FIFO Data Swap Write-Only */ +#define SPICC_DLYCTL_RO_MASK GENMASK(20, 15) /* Delay Control Read-Only */ +#define SPICC_DLYCTL_W1_MASK GENMASK(21, 16) /* Delay Control Write-Only */ +#define SPICC_FIFORST_RO_MASK GENMASK(22, 21) /* FIFO Softreset Read-Only */ +#define SPICC_FIFORST_W1_MASK GENMASK(23, 22) /* FIFO Softreset Write-Only */ + +#define SPICC_DRADDR 0x20 /* Read Address of DMA */ + +#define SPICC_DWADDR 0x24 /* Write Address of DMA */ + +#define writel_bits_relaxed(mask, val, addr) \ + writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr) + +#define SPICC_BURST_MAX 16 +#define SPICC_FIFO_HALF 10 + +struct meson_spicc_device { + struct spi_master *master; + struct platform_device *pdev; + void __iomem *base; + struct clk *core; + struct spi_message *message; + struct spi_transfer *xfer; + u8 *tx_buf; + u8 *rx_buf; + unsigned int bytes_per_word; + unsigned long tx_remain; + unsigned long txb_remain; + unsigned long rx_remain; + unsigned long rxb_remain; + unsigned long xfer_remain; + bool is_burst_end; + bool is_last_burst; +}; + +static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc) +{ + return !!FIELD_GET(SPICC_TF, + readl_relaxed(spicc->base + SPICC_STATREG)); +} + +static inline bool meson_spicc_rxready(struct meson_spicc_device *spicc) +{ + return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF_EN, + readl_relaxed(spicc->base + SPICC_STATREG)); +} + +static inline u32 meson_spicc_pull_data(struct meson_spicc_device *spicc) +{ + unsigned int bytes = spicc->bytes_per_word; + unsigned int byte_shift = 0; + u32 data = 0; + u8 byte; + + while (bytes--) { + byte = *spicc->tx_buf++; + data |= (byte & 0xff) << byte_shift; + byte_shift += 8; + } + + spicc->tx_remain--; + return data; +} + +static inline void meson_spicc_push_data(struct meson_spicc_device *spicc, + u32 data) +{ + unsigned int bytes = spicc->bytes_per_word; + unsigned int byte_shift = 0; + u8 byte; + + while (bytes--) { + byte = (data >> byte_shift) & 0xff; + *spicc->rx_buf++ = byte; + byte_shift += 8; + } + + spicc->rx_remain--; +} + +static inline void meson_spicc_rx(struct meson_spicc_device *spicc) +{ + /* Empty RX FIFO */ + while (spicc->rx_remain && + meson_spicc_rxready(spicc)) + meson_spicc_push_data(spicc, + readl_relaxed(spicc->base + SPICC_RXDATA)); +} + +static inline void meson_spicc_tx(struct meson_spicc_device *spicc) +{ + /* Fill Up TX FIFO */ + while (spicc->tx_remain && + !meson_spicc_txfull(spicc)) + writel_relaxed(meson_spicc_pull_data(spicc), + spicc->base + SPICC_TXDATA); +} + +static inline u32 meson_spicc_setup_rx_irq(struct meson_spicc_device *spicc, + u32 irq_ctrl) +{ + if (spicc->rx_remain > SPICC_FIFO_HALF) + irq_ctrl |= SPICC_RH_EN; + else + irq_ctrl |= SPICC_RR_EN; + + return irq_ctrl; +} + +static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc, + unsigned int burst_len) +{ + /* Setup Xfer variables */ + spicc->tx_remain = burst_len; + spicc->rx_remain = burst_len; + spicc->xfer_remain -= burst_len * spicc->bytes_per_word; + spicc->is_burst_end = false; + if (burst_len < SPICC_BURST_MAX || !spicc->xfer_remain) + spicc->is_last_burst = true; + else + spicc->is_last_burst = false; + + /* Setup burst length */ + writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, + FIELD_PREP(SPICC_BURSTLENGTH_MASK, + burst_len), + spicc->base + SPICC_CONREG); + + /* Fill TX FIFO */ + meson_spicc_tx(spicc); +} + +static irqreturn_t meson_spicc_irq(int irq, void *data) +{ + struct meson_spicc_device *spicc = (void *) data; + u32 ctrl = readl_relaxed(spicc->base + SPICC_INTREG); + u32 stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl; + + ctrl &= ~(SPICC_RH_EN | SPICC_RR_EN); + + /* Empty RX FIFO */ + meson_spicc_rx(spicc); + + /* Enable TC interrupt since we transferred everything */ + if (!spicc->tx_remain && !spicc->rx_remain) { + spicc->is_burst_end = true; + + /* Enable TC interrupt */ + ctrl |= SPICC_TC_EN; + + /* Reload IRQ status */ + stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl; + } + + /* Check transfer complete */ + if ((stat & SPICC_TC) && spicc->is_burst_end) { + unsigned int burst_len; + + /* Clear TC bit */ + writel_relaxed(SPICC_TC, spicc->base + SPICC_STATREG); + + /* Disable TC interrupt */ + ctrl &= ~SPICC_TC_EN; + + if (spicc->is_last_burst) { + /* Disable all IRQs */ + writel(0, spicc->base + SPICC_INTREG); + + spi_finalize_current_transfer(spicc->master); + + return IRQ_HANDLED; + } + + burst_len = min_t(unsigned int, + spicc->xfer_remain / spicc->bytes_per_word, + SPICC_BURST_MAX); + + /* Setup burst */ + meson_spicc_setup_burst(spicc, burst_len); + + /* Restart burst */ + writel_bits_relaxed(SPICC_XCH, SPICC_XCH, + spicc->base + SPICC_CONREG); + } + + /* Setup RX interrupt trigger */ + ctrl = meson_spicc_setup_rx_irq(spicc, ctrl); + + /* Reconfigure interrupts */ + writel(ctrl, spicc->base + SPICC_INTREG); + + return IRQ_HANDLED; +} + +static u32 meson_spicc_setup_speed(struct meson_spicc_device *spicc, u32 conf, + u32 speed) +{ + unsigned long parent, value; + unsigned int i, div; + + parent = clk_get_rate(spicc->core); + + /* Find closest inferior/equal possible speed */ + for (i = 0 ; i < 7 ; ++i) { + /* 2^(data_rate+2) */ + value = parent >> (i + 2); + + if (value <= speed) + break; + } + + /* If provided speed it lower than max divider, use max divider */ + if (i > 7) { + div = 7; + dev_warn_once(&spicc->pdev->dev, "unable to get close to speed %u\n", + speed); + } else + div = i; + + dev_dbg(&spicc->pdev->dev, "parent %lu, speed %u -> %lu (%u)\n", + parent, speed, value, div); + + conf &= ~SPICC_DATARATE_MASK; + conf |= FIELD_PREP(SPICC_DATARATE_MASK, div); + + return conf; +} + +static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc, + struct spi_transfer *xfer) +{ + u32 conf, conf_orig; + + /* Read original configuration */ + conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG); + + /* Select closest divider */ + conf = meson_spicc_setup_speed(spicc, conf, xfer->speed_hz); + + /* Setup word width */ + conf &= ~SPICC_BITLENGTH_MASK; + conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, + (spicc->bytes_per_word << 3) - 1); + + /* Ignore if unchanged */ + if (conf != conf_orig) + writel_relaxed(conf, spicc->base + SPICC_CONREG); +} + +static int meson_spicc_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct meson_spicc_device *spicc = spi_master_get_devdata(master); + unsigned int burst_len; + u32 irq = 0; + + /* Store current transfer */ + spicc->xfer = xfer; + + /* Setup transfer parameters */ + spicc->tx_buf = (u8 *)xfer->tx_buf; + spicc->rx_buf = (u8 *)xfer->rx_buf; + spicc->xfer_remain = xfer->len; + + /* Pre-calculate word size */ + spicc->bytes_per_word = + DIV_ROUND_UP(spicc->xfer->bits_per_word, 8); + + /* Setup transfer parameters */ + meson_spicc_setup_xfer(spicc, xfer); + + burst_len = min_t(unsigned int, + spicc->xfer_remain / spicc->bytes_per_word, + SPICC_BURST_MAX); + + meson_spicc_setup_burst(spicc, burst_len); + + irq = meson_spicc_setup_rx_irq(spicc, irq); + + /* Start burst */ + writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG); + + /* Enable interrupts */ + writel_relaxed(irq, spicc->base + SPICC_INTREG); + + return 1; +} + +static int meson_spicc_prepare_message(struct spi_master *master, + struct spi_message *message) +{ + struct meson_spicc_device *spicc = spi_master_get_devdata(master); + struct spi_device *spi = message->spi; + u32 conf = 0; + + /* Store current message */ + spicc->message = message; + + /* Enable Master */ + conf |= SPICC_ENABLE; + conf |= SPICC_MODE_MASTER; + + /* SMC = 0 */ + + /* Setup transfer mode */ |
