X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fspi%2Fmxs_spi.c;h=627644b56bdf13f94fbdbae4d10f80cbbe06f0d1;hb=5e4e87418e0f0e62854fe6c38736e2ee771ec3a7;hp=a037c130efa5c09557c4be0a5552129222fc2759;hpb=fa7a51cb8272bd6076ea4701fd6bdc65a68703ba;p=oweals%2Fu-boot.git diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index a037c130ef..627644b56b 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -4,20 +4,7 @@ * Copyright (C) 2011 Marek Vasut * 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. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ * * NOTE: This driver only supports the SPI-controller chipselects, * GPIO driven chipselects are not supported. @@ -25,13 +12,14 @@ #include #include +#include #include #include #include #include #include #include -#include +#include #define MXS_SPI_MAX_TIMEOUT 1000000 #define MXS_SPI_PORT_OFFSET 0x2000 @@ -40,23 +28,11 @@ #define MXSSSP_SMALL_TRANSFER 512 -/* - * CONFIG_MXS_SPI_DMA_ENABLE: Experimental mixed PIO/DMA support for MXS SPI - * host. Use with utmost caution! - * - * Enabling this is not yet recommended since this - * still doesn't support transfers to/from unaligned - * addresses. Therefore this driver will not work - * for example with saving environment. This is - * caused by DMA alignment constraints on MXS. - */ - struct mxs_spi_slave { struct spi_slave slave; uint32_t max_khz; uint32_t mode; struct mxs_ssp_regs *regs; - struct mxs_dma_desc *desc; }; static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave) @@ -71,7 +47,7 @@ void spi_init(void) int spi_cs_is_valid(unsigned int bus, unsigned int cs) { /* MXS SPI: 4 ports and 3 chip selects maximum */ - if (bus > 3 || cs > 2) + if (!mxs_ssp_bus_id_valid(bus) || cs > 2) return 0; else return 1; @@ -81,47 +57,26 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int mode) { struct mxs_spi_slave *mxs_slave; - uint32_t addr; - struct mxs_ssp_regs *ssp_regs; - int reg; - struct mxs_dma_desc *desc; if (!spi_cs_is_valid(bus, cs)) { printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs); return NULL; } - mxs_slave = malloc(sizeof(struct mxs_spi_slave)); + mxs_slave = spi_alloc_slave(struct mxs_spi_slave, bus, cs); if (!mxs_slave) return NULL; - desc = mxs_dma_desc_alloc(); - if (!desc) - goto err_desc; - - if (mxs_dma_init_channel(bus)) + if (mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + bus)) goto err_init; - addr = MXS_SSP0_BASE + (bus * MXS_SPI_PORT_OFFSET); - - mxs_slave->slave.bus = bus; - mxs_slave->slave.cs = cs; mxs_slave->max_khz = max_hz / 1000; mxs_slave->mode = mode; - mxs_slave->regs = (struct mxs_ssp_regs *)addr; - mxs_slave->desc = desc; - ssp_regs = mxs_slave->regs; + mxs_slave->regs = mxs_ssp_regs_by_bus(bus); - reg = readl(&ssp_regs->hw_ssp_ctrl0); - reg &= ~(MXS_SSP_CHIPSELECT_MASK); - reg |= cs << MXS_SSP_CHIPSELECT_SHIFT; - - writel(reg, &ssp_regs->hw_ssp_ctrl0); return &mxs_slave->slave; err_init: - mxs_dma_desc_free(desc); -err_desc: free(mxs_slave); return NULL; } @@ -129,7 +84,6 @@ err_desc: void spi_free_slave(struct spi_slave *slave) { struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); - mxs_dma_desc_free(mxs_slave->desc); free(mxs_slave); } @@ -141,7 +95,9 @@ int spi_claim_bus(struct spi_slave *slave) mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); - writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0); + writel((slave->cs << MXS_SSP_CHIPSELECT_SHIFT) | + SSP_CTRL0_BUS_WIDTH_ONE_BIT, + &ssp_regs->hw_ssp_ctrl0); reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS; reg |= (mxs_slave->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0; @@ -150,7 +106,7 @@ int spi_claim_bus(struct spi_slave *slave) writel(0, &ssp_regs->hw_ssp_cmd0); - mx28_set_ssp_busclock(slave->bus, mxs_slave->max_khz); + mxs_set_ssp_busclock(slave->bus, mxs_slave->max_khz); return 0; } @@ -181,7 +137,12 @@ static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave, while (length--) { /* We transfer 1 byte */ +#if defined(CONFIG_MX23) + writel(SSP_CTRL0_XFER_COUNT_MASK, &ssp_regs->hw_ssp_ctrl0_clr); + writel(1, &ssp_regs->hw_ssp_ctrl0_set); +#elif defined(CONFIG_MX28) writel(1, &ssp_regs->hw_ssp_xfer_size); +#endif if ((flags & SPI_XFER_END) && !length) mxs_spi_end_xfer(ssp_regs); @@ -228,60 +189,122 @@ static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave, static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave, char *data, int length, int write, unsigned long flags) { - struct mxs_dma_desc *desc = slave->desc; + const int xfer_max_sz = 0xff00; + const int desc_count = DIV_ROUND_UP(length, xfer_max_sz) + 1; struct mxs_ssp_regs *ssp_regs = slave->regs; - uint32_t ctrl0 = SSP_CTRL0_DATA_XFER; + struct mxs_dma_desc *dp; + uint32_t ctrl0; uint32_t cache_data_count; + const uint32_t dstart = (uint32_t)data; int dmach; + int tl; + int ret = 0; - memset(desc, 0, sizeof(struct mxs_dma_desc)); - desc->address = (dma_addr_t)desc; +#if defined(CONFIG_MX23) + const int mxs_spi_pio_words = 1; +#elif defined(CONFIG_MX28) + const int mxs_spi_pio_words = 4; +#endif + + ALLOC_CACHE_ALIGN_BUFFER(struct mxs_dma_desc, desc, desc_count); + + memset(desc, 0, sizeof(struct mxs_dma_desc) * desc_count); + + ctrl0 = readl(&ssp_regs->hw_ssp_ctrl0); + ctrl0 |= SSP_CTRL0_DATA_XFER; if (flags & SPI_XFER_BEGIN) ctrl0 |= SSP_CTRL0_LOCK_CS; - if (flags & SPI_XFER_END) - ctrl0 |= SSP_CTRL0_IGNORE_CRC; if (!write) ctrl0 |= SSP_CTRL0_READ; - writel(length, &ssp_regs->hw_ssp_xfer_size); - if (length % ARCH_DMA_MINALIGN) cache_data_count = roundup(length, ARCH_DMA_MINALIGN); else cache_data_count = length; - if (!write) { - slave->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE; - slave->desc->cmd.address = (dma_addr_t)data; - } else { - slave->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ; - slave->desc->cmd.address = (dma_addr_t)data; + /* Flush data to DRAM so DMA can pick them up */ + if (write) + flush_dcache_range(dstart, dstart + cache_data_count); - /* Flush data to DRAM so DMA can pick them up */ - flush_dcache_range((uint32_t)data, - (uint32_t)(data + cache_data_count)); - } + /* Invalidate the area, so no writeback into the RAM races with DMA */ + invalidate_dcache_range(dstart, dstart + cache_data_count); - slave->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM | - (length << MXS_DMA_DESC_BYTES_OFFSET) | - (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | - MXS_DMA_DESC_WAIT4END; + dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus; - slave->desc->cmd.pio_words[0] = ctrl0; + dp = desc; + while (length) { + dp->address = (dma_addr_t)dp; + dp->cmd.address = (dma_addr_t)data; + + /* + * This is correct, even though it does indeed look insane. + * I hereby have to, wholeheartedly, thank Freescale Inc., + * for always inventing insane hardware and keeping me busy + * and employed ;-) + */ + if (write) + dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ; + else + dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE; + + /* + * The DMA controller can transfer large chunks (64kB) at + * time by setting the transfer length to 0. Setting tl to + * 0x10000 will overflow below and make .data contain 0. + * Otherwise, 0xff00 is the transfer maximum. + */ + if (length >= 0x10000) + tl = 0x10000; + else + tl = min(length, xfer_max_sz); + + dp->cmd.data |= + ((tl & 0xffff) << MXS_DMA_DESC_BYTES_OFFSET) | + (mxs_spi_pio_words << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + MXS_DMA_DESC_HALT_ON_TERMINATE | + MXS_DMA_DESC_TERMINATE_FLUSH; + + data += tl; + length -= tl; + + if (!length) { + dp->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; + + if (flags & SPI_XFER_END) { + ctrl0 &= ~SSP_CTRL0_LOCK_CS; + ctrl0 |= SSP_CTRL0_IGNORE_CRC; + } + } + + /* + * Write CTRL0, CMD0, CMD1 and XFER_SIZE registers in + * case of MX28, write only CTRL0 in case of MX23 due + * to the difference in register layout. It is utterly + * essential that the XFER_SIZE register is written on + * a per-descriptor basis with the same size as is the + * descriptor! + */ + dp->cmd.pio_words[0] = ctrl0; +#ifdef CONFIG_MX28 + dp->cmd.pio_words[1] = 0; + dp->cmd.pio_words[2] = 0; + dp->cmd.pio_words[3] = tl; +#endif + + mxs_dma_desc_append(dmach, dp); + + dp++; + } - dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus; - mxs_dma_desc_append(dmach, slave->desc); if (mxs_dma_go(dmach)) - return -EINVAL; + ret = -EINVAL; /* The data arrived into DRAM, invalidate cache over them */ - if (!write) { - invalidate_dcache_range((uint32_t)data, - (uint32_t)(data + cache_data_count)); - } + if (!write) + invalidate_dcache_range(dstart, dstart + cache_data_count); - return 0; + return ret; } int spi_xfer(struct spi_slave *slave, unsigned int bitlen, @@ -293,12 +316,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, char dummy; int write = 0; char *data = NULL; - -#ifdef CONFIG_MXS_SPI_DMA_ENABLE int dma = 1; -#else - int dma = 0; -#endif if (bitlen == 0) { if (flags & SPI_XFER_END) {