162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Freescale MXS SPI master driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright 2012 DENX Software Engineering, GmbH. 662306a36Sopenharmony_ci// Copyright 2012 Freescale Semiconductor, Inc. 762306a36Sopenharmony_ci// Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. 862306a36Sopenharmony_ci// 962306a36Sopenharmony_ci// Rework and transition to new API by: 1062306a36Sopenharmony_ci// Marek Vasut <marex@denx.de> 1162306a36Sopenharmony_ci// 1262306a36Sopenharmony_ci// Based on previous attempt by: 1362306a36Sopenharmony_ci// Fabio Estevam <fabio.estevam@freescale.com> 1462306a36Sopenharmony_ci// 1562306a36Sopenharmony_ci// Based on code from U-Boot bootloader by: 1662306a36Sopenharmony_ci// Marek Vasut <marex@denx.de> 1762306a36Sopenharmony_ci// 1862306a36Sopenharmony_ci// Based on spi-stmp.c, which is: 1962306a36Sopenharmony_ci// Author: Dmitry Pervushin <dimka@embeddedalley.com> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/ioport.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/of_device.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/delay.h> 2762306a36Sopenharmony_ci#include <linux/interrupt.h> 2862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2962306a36Sopenharmony_ci#include <linux/dmaengine.h> 3062306a36Sopenharmony_ci#include <linux/highmem.h> 3162306a36Sopenharmony_ci#include <linux/clk.h> 3262306a36Sopenharmony_ci#include <linux/err.h> 3362306a36Sopenharmony_ci#include <linux/completion.h> 3462306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 3562306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 3662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 3762306a36Sopenharmony_ci#include <linux/module.h> 3862306a36Sopenharmony_ci#include <linux/stmp_device.h> 3962306a36Sopenharmony_ci#include <linux/spi/spi.h> 4062306a36Sopenharmony_ci#include <linux/spi/mxs-spi.h> 4162306a36Sopenharmony_ci#include <trace/events/spi.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define DRIVER_NAME "mxs-spi" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* Use 10S timeout for very long transfers, it should suffice. */ 4662306a36Sopenharmony_ci#define SSP_TIMEOUT 10000 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define SG_MAXLEN 0xff00 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * Flags for txrx functions. More efficient that using an argument register for 5262306a36Sopenharmony_ci * each one. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci#define TXRX_WRITE (1<<0) /* This is a write */ 5562306a36Sopenharmony_ci#define TXRX_DEASSERT_CS (1<<1) /* De-assert CS at end of txrx */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct mxs_spi { 5862306a36Sopenharmony_ci struct mxs_ssp ssp; 5962306a36Sopenharmony_ci struct completion c; 6062306a36Sopenharmony_ci unsigned int sck; /* Rate requested (vs actual) */ 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int mxs_spi_setup_transfer(struct spi_device *dev, 6462306a36Sopenharmony_ci const struct spi_transfer *t) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct mxs_spi *spi = spi_master_get_devdata(dev->master); 6762306a36Sopenharmony_ci struct mxs_ssp *ssp = &spi->ssp; 6862306a36Sopenharmony_ci const unsigned int hz = min(dev->max_speed_hz, t->speed_hz); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (hz == 0) { 7162306a36Sopenharmony_ci dev_err(&dev->dev, "SPI clock rate of zero not allowed\n"); 7262306a36Sopenharmony_ci return -EINVAL; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (hz != spi->sck) { 7662306a36Sopenharmony_ci mxs_ssp_set_clk_rate(ssp, hz); 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * Save requested rate, hz, rather than the actual rate, 7962306a36Sopenharmony_ci * ssp->clk_rate. Otherwise we would set the rate every transfer 8062306a36Sopenharmony_ci * when the actual rate is not quite the same as requested rate. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci spi->sck = hz; 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * Perhaps we should return an error if the actual clock is 8562306a36Sopenharmony_ci * nowhere close to what was requested? 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci writel(BM_SSP_CTRL0_LOCK_CS, 9062306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) | 9362306a36Sopenharmony_ci BF_SSP_CTRL1_WORD_LENGTH(BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) | 9462306a36Sopenharmony_ci ((dev->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | 9562306a36Sopenharmony_ci ((dev->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0), 9662306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL1(ssp)); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci writel(0x0, ssp->base + HW_SSP_CMD0); 9962306a36Sopenharmony_ci writel(0x0, ssp->base + HW_SSP_CMD1); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic u32 mxs_spi_cs_to_reg(unsigned cs) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci u32 select = 0; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci * i.MX28 Datasheet: 17.10.1: HW_SSP_CTRL0 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * The bits BM_SSP_CTRL0_WAIT_FOR_CMD and BM_SSP_CTRL0_WAIT_FOR_IRQ 11262306a36Sopenharmony_ci * in HW_SSP_CTRL0 register do have multiple usage, please refer to 11362306a36Sopenharmony_ci * the datasheet for further details. In SPI mode, they are used to 11462306a36Sopenharmony_ci * toggle the chip-select lines (nCS pins). 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci if (cs & 1) 11762306a36Sopenharmony_ci select |= BM_SSP_CTRL0_WAIT_FOR_CMD; 11862306a36Sopenharmony_ci if (cs & 2) 11962306a36Sopenharmony_ci select |= BM_SSP_CTRL0_WAIT_FOR_IRQ; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return select; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); 12762306a36Sopenharmony_ci struct mxs_ssp *ssp = &spi->ssp; 12862306a36Sopenharmony_ci u32 reg; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci do { 13162306a36Sopenharmony_ci reg = readl_relaxed(ssp->base + offset); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!set) 13462306a36Sopenharmony_ci reg = ~reg; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci reg &= mask; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (reg == mask) 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci } while (time_before(jiffies, timeout)); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return -ETIMEDOUT; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void mxs_ssp_dma_irq_callback(void *param) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct mxs_spi *spi = param; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci complete(&spi->c); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct mxs_ssp *ssp = dev_id; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci dev_err(ssp->dev, "%s[%i] CTRL1=%08x STATUS=%08x\n", 15762306a36Sopenharmony_ci __func__, __LINE__, 15862306a36Sopenharmony_ci readl(ssp->base + HW_SSP_CTRL1(ssp)), 15962306a36Sopenharmony_ci readl(ssp->base + HW_SSP_STATUS(ssp))); 16062306a36Sopenharmony_ci return IRQ_HANDLED; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int mxs_spi_txrx_dma(struct mxs_spi *spi, 16462306a36Sopenharmony_ci unsigned char *buf, int len, 16562306a36Sopenharmony_ci unsigned int flags) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct mxs_ssp *ssp = &spi->ssp; 16862306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc = NULL; 16962306a36Sopenharmony_ci const bool vmalloced_buf = is_vmalloc_addr(buf); 17062306a36Sopenharmony_ci const int desc_len = vmalloced_buf ? PAGE_SIZE : SG_MAXLEN; 17162306a36Sopenharmony_ci const int sgs = DIV_ROUND_UP(len, desc_len); 17262306a36Sopenharmony_ci int sg_count; 17362306a36Sopenharmony_ci int min, ret; 17462306a36Sopenharmony_ci u32 ctrl0; 17562306a36Sopenharmony_ci struct page *vm_page; 17662306a36Sopenharmony_ci struct { 17762306a36Sopenharmony_ci u32 pio[4]; 17862306a36Sopenharmony_ci struct scatterlist sg; 17962306a36Sopenharmony_ci } *dma_xfer; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (!len) 18262306a36Sopenharmony_ci return -EINVAL; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci dma_xfer = kcalloc(sgs, sizeof(*dma_xfer), GFP_KERNEL); 18562306a36Sopenharmony_ci if (!dma_xfer) 18662306a36Sopenharmony_ci return -ENOMEM; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci reinit_completion(&spi->c); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Chip select was already programmed into CTRL0 */ 19162306a36Sopenharmony_ci ctrl0 = readl(ssp->base + HW_SSP_CTRL0); 19262306a36Sopenharmony_ci ctrl0 &= ~(BM_SSP_CTRL0_XFER_COUNT | BM_SSP_CTRL0_IGNORE_CRC | 19362306a36Sopenharmony_ci BM_SSP_CTRL0_READ); 19462306a36Sopenharmony_ci ctrl0 |= BM_SSP_CTRL0_DATA_XFER; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!(flags & TXRX_WRITE)) 19762306a36Sopenharmony_ci ctrl0 |= BM_SSP_CTRL0_READ; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Queue the DMA data transfer. */ 20062306a36Sopenharmony_ci for (sg_count = 0; sg_count < sgs; sg_count++) { 20162306a36Sopenharmony_ci /* Prepare the transfer descriptor. */ 20262306a36Sopenharmony_ci min = min(len, desc_len); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * De-assert CS on last segment if flag is set (i.e., no more 20662306a36Sopenharmony_ci * transfers will follow) 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci if ((sg_count + 1 == sgs) && (flags & TXRX_DEASSERT_CS)) 20962306a36Sopenharmony_ci ctrl0 |= BM_SSP_CTRL0_IGNORE_CRC; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (ssp->devid == IMX23_SSP) { 21262306a36Sopenharmony_ci ctrl0 &= ~BM_SSP_CTRL0_XFER_COUNT; 21362306a36Sopenharmony_ci ctrl0 |= min; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci dma_xfer[sg_count].pio[0] = ctrl0; 21762306a36Sopenharmony_ci dma_xfer[sg_count].pio[3] = min; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (vmalloced_buf) { 22062306a36Sopenharmony_ci vm_page = vmalloc_to_page(buf); 22162306a36Sopenharmony_ci if (!vm_page) { 22262306a36Sopenharmony_ci ret = -ENOMEM; 22362306a36Sopenharmony_ci goto err_vmalloc; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci sg_init_table(&dma_xfer[sg_count].sg, 1); 22762306a36Sopenharmony_ci sg_set_page(&dma_xfer[sg_count].sg, vm_page, 22862306a36Sopenharmony_ci min, offset_in_page(buf)); 22962306a36Sopenharmony_ci } else { 23062306a36Sopenharmony_ci sg_init_one(&dma_xfer[sg_count].sg, buf, min); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci ret = dma_map_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, 23462306a36Sopenharmony_ci (flags & TXRX_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci len -= min; 23762306a36Sopenharmony_ci buf += min; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Queue the PIO register write transfer. */ 24062306a36Sopenharmony_ci desc = dmaengine_prep_slave_sg(ssp->dmach, 24162306a36Sopenharmony_ci (struct scatterlist *)dma_xfer[sg_count].pio, 24262306a36Sopenharmony_ci (ssp->devid == IMX23_SSP) ? 1 : 4, 24362306a36Sopenharmony_ci DMA_TRANS_NONE, 24462306a36Sopenharmony_ci sg_count ? DMA_PREP_INTERRUPT : 0); 24562306a36Sopenharmony_ci if (!desc) { 24662306a36Sopenharmony_ci dev_err(ssp->dev, 24762306a36Sopenharmony_ci "Failed to get PIO reg. write descriptor.\n"); 24862306a36Sopenharmony_ci ret = -EINVAL; 24962306a36Sopenharmony_ci goto err_mapped; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci desc = dmaengine_prep_slave_sg(ssp->dmach, 25362306a36Sopenharmony_ci &dma_xfer[sg_count].sg, 1, 25462306a36Sopenharmony_ci (flags & TXRX_WRITE) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, 25562306a36Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (!desc) { 25862306a36Sopenharmony_ci dev_err(ssp->dev, 25962306a36Sopenharmony_ci "Failed to get DMA data write descriptor.\n"); 26062306a36Sopenharmony_ci ret = -EINVAL; 26162306a36Sopenharmony_ci goto err_mapped; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* 26662306a36Sopenharmony_ci * The last descriptor must have this callback, 26762306a36Sopenharmony_ci * to finish the DMA transaction. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci desc->callback = mxs_ssp_dma_irq_callback; 27062306a36Sopenharmony_ci desc->callback_param = spi; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Start the transfer. */ 27362306a36Sopenharmony_ci dmaengine_submit(desc); 27462306a36Sopenharmony_ci dma_async_issue_pending(ssp->dmach); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (!wait_for_completion_timeout(&spi->c, 27762306a36Sopenharmony_ci msecs_to_jiffies(SSP_TIMEOUT))) { 27862306a36Sopenharmony_ci dev_err(ssp->dev, "DMA transfer timeout\n"); 27962306a36Sopenharmony_ci ret = -ETIMEDOUT; 28062306a36Sopenharmony_ci dmaengine_terminate_all(ssp->dmach); 28162306a36Sopenharmony_ci goto err_vmalloc; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ret = 0; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cierr_vmalloc: 28762306a36Sopenharmony_ci while (--sg_count >= 0) { 28862306a36Sopenharmony_cierr_mapped: 28962306a36Sopenharmony_ci dma_unmap_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, 29062306a36Sopenharmony_ci (flags & TXRX_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci kfree(dma_xfer); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return ret; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int mxs_spi_txrx_pio(struct mxs_spi *spi, 29962306a36Sopenharmony_ci unsigned char *buf, int len, 30062306a36Sopenharmony_ci unsigned int flags) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct mxs_ssp *ssp = &spi->ssp; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci writel(BM_SSP_CTRL0_IGNORE_CRC, 30562306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci while (len--) { 30862306a36Sopenharmony_ci if (len == 0 && (flags & TXRX_DEASSERT_CS)) 30962306a36Sopenharmony_ci writel(BM_SSP_CTRL0_IGNORE_CRC, 31062306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (ssp->devid == IMX23_SSP) { 31362306a36Sopenharmony_ci writel(BM_SSP_CTRL0_XFER_COUNT, 31462306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); 31562306a36Sopenharmony_ci writel(1, 31662306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 31762306a36Sopenharmony_ci } else { 31862306a36Sopenharmony_ci writel(1, ssp->base + HW_SSP_XFER_SIZE); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (flags & TXRX_WRITE) 32262306a36Sopenharmony_ci writel(BM_SSP_CTRL0_READ, 32362306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); 32462306a36Sopenharmony_ci else 32562306a36Sopenharmony_ci writel(BM_SSP_CTRL0_READ, 32662306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci writel(BM_SSP_CTRL0_RUN, 32962306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 1)) 33262306a36Sopenharmony_ci return -ETIMEDOUT; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (flags & TXRX_WRITE) 33562306a36Sopenharmony_ci writel(*buf, ssp->base + HW_SSP_DATA(ssp)); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci writel(BM_SSP_CTRL0_DATA_XFER, 33862306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (!(flags & TXRX_WRITE)) { 34162306a36Sopenharmony_ci if (mxs_ssp_wait(spi, HW_SSP_STATUS(ssp), 34262306a36Sopenharmony_ci BM_SSP_STATUS_FIFO_EMPTY, 0)) 34362306a36Sopenharmony_ci return -ETIMEDOUT; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci *buf = (readl(ssp->base + HW_SSP_DATA(ssp)) & 0xff); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 0)) 34962306a36Sopenharmony_ci return -ETIMEDOUT; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci buf++; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (len <= 0) 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return -ETIMEDOUT; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int mxs_spi_transfer_one(struct spi_master *master, 36162306a36Sopenharmony_ci struct spi_message *m) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct mxs_spi *spi = spi_master_get_devdata(master); 36462306a36Sopenharmony_ci struct mxs_ssp *ssp = &spi->ssp; 36562306a36Sopenharmony_ci struct spi_transfer *t; 36662306a36Sopenharmony_ci unsigned int flag; 36762306a36Sopenharmony_ci int status = 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Program CS register bits here, it will be used for all transfers. */ 37062306a36Sopenharmony_ci writel(BM_SSP_CTRL0_WAIT_FOR_CMD | BM_SSP_CTRL0_WAIT_FOR_IRQ, 37162306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); 37262306a36Sopenharmony_ci writel(mxs_spi_cs_to_reg(spi_get_chipselect(m->spi, 0)), 37362306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci list_for_each_entry(t, &m->transfers, transfer_list) { 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci trace_spi_transfer_start(m, t); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci status = mxs_spi_setup_transfer(m->spi, t); 38062306a36Sopenharmony_ci if (status) 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* De-assert on last transfer, inverted by cs_change flag */ 38462306a36Sopenharmony_ci flag = (&t->transfer_list == m->transfers.prev) ^ t->cs_change ? 38562306a36Sopenharmony_ci TXRX_DEASSERT_CS : 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* 38862306a36Sopenharmony_ci * Small blocks can be transfered via PIO. 38962306a36Sopenharmony_ci * Measured by empiric means: 39062306a36Sopenharmony_ci * 39162306a36Sopenharmony_ci * dd if=/dev/mtdblock0 of=/dev/null bs=1024k count=1 39262306a36Sopenharmony_ci * 39362306a36Sopenharmony_ci * DMA only: 2.164808 seconds, 473.0KB/s 39462306a36Sopenharmony_ci * Combined: 1.676276 seconds, 610.9KB/s 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci if (t->len < 32) { 39762306a36Sopenharmony_ci writel(BM_SSP_CTRL1_DMA_ENABLE, 39862306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL1(ssp) + 39962306a36Sopenharmony_ci STMP_OFFSET_REG_CLR); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (t->tx_buf) 40262306a36Sopenharmony_ci status = mxs_spi_txrx_pio(spi, 40362306a36Sopenharmony_ci (void *)t->tx_buf, 40462306a36Sopenharmony_ci t->len, flag | TXRX_WRITE); 40562306a36Sopenharmony_ci if (t->rx_buf) 40662306a36Sopenharmony_ci status = mxs_spi_txrx_pio(spi, 40762306a36Sopenharmony_ci t->rx_buf, t->len, 40862306a36Sopenharmony_ci flag); 40962306a36Sopenharmony_ci } else { 41062306a36Sopenharmony_ci writel(BM_SSP_CTRL1_DMA_ENABLE, 41162306a36Sopenharmony_ci ssp->base + HW_SSP_CTRL1(ssp) + 41262306a36Sopenharmony_ci STMP_OFFSET_REG_SET); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (t->tx_buf) 41562306a36Sopenharmony_ci status = mxs_spi_txrx_dma(spi, 41662306a36Sopenharmony_ci (void *)t->tx_buf, t->len, 41762306a36Sopenharmony_ci flag | TXRX_WRITE); 41862306a36Sopenharmony_ci if (t->rx_buf) 41962306a36Sopenharmony_ci status = mxs_spi_txrx_dma(spi, 42062306a36Sopenharmony_ci t->rx_buf, t->len, 42162306a36Sopenharmony_ci flag); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci trace_spi_transfer_stop(m, t); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (status) { 42762306a36Sopenharmony_ci stmp_reset_block(ssp->base); 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci m->actual_length += t->len; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci m->status = status; 43562306a36Sopenharmony_ci spi_finalize_current_message(master); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return status; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic int mxs_spi_runtime_suspend(struct device *dev) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 44362306a36Sopenharmony_ci struct mxs_spi *spi = spi_master_get_devdata(master); 44462306a36Sopenharmony_ci struct mxs_ssp *ssp = &spi->ssp; 44562306a36Sopenharmony_ci int ret; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci clk_disable_unprepare(ssp->clk); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci ret = pinctrl_pm_select_idle_state(dev); 45062306a36Sopenharmony_ci if (ret) { 45162306a36Sopenharmony_ci int ret2 = clk_prepare_enable(ssp->clk); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (ret2) 45462306a36Sopenharmony_ci dev_warn(dev, "Failed to reenable clock after failing pinctrl request (pinctrl: %d, clk: %d)\n", 45562306a36Sopenharmony_ci ret, ret2); 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return ret; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int mxs_spi_runtime_resume(struct device *dev) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 46462306a36Sopenharmony_ci struct mxs_spi *spi = spi_master_get_devdata(master); 46562306a36Sopenharmony_ci struct mxs_ssp *ssp = &spi->ssp; 46662306a36Sopenharmony_ci int ret; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci ret = pinctrl_pm_select_default_state(dev); 46962306a36Sopenharmony_ci if (ret) 47062306a36Sopenharmony_ci return ret; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ret = clk_prepare_enable(ssp->clk); 47362306a36Sopenharmony_ci if (ret) 47462306a36Sopenharmony_ci pinctrl_pm_select_idle_state(dev); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return ret; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int __maybe_unused mxs_spi_suspend(struct device *dev) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 48262306a36Sopenharmony_ci int ret; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci ret = spi_master_suspend(master); 48562306a36Sopenharmony_ci if (ret) 48662306a36Sopenharmony_ci return ret; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (!pm_runtime_suspended(dev)) 48962306a36Sopenharmony_ci return mxs_spi_runtime_suspend(dev); 49062306a36Sopenharmony_ci else 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int __maybe_unused mxs_spi_resume(struct device *dev) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 49762306a36Sopenharmony_ci int ret; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (!pm_runtime_suspended(dev)) 50062306a36Sopenharmony_ci ret = mxs_spi_runtime_resume(dev); 50162306a36Sopenharmony_ci else 50262306a36Sopenharmony_ci ret = 0; 50362306a36Sopenharmony_ci if (ret) 50462306a36Sopenharmony_ci return ret; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ret = spi_master_resume(master); 50762306a36Sopenharmony_ci if (ret < 0 && !pm_runtime_suspended(dev)) 50862306a36Sopenharmony_ci mxs_spi_runtime_suspend(dev); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return ret; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic const struct dev_pm_ops mxs_spi_pm = { 51462306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(mxs_spi_runtime_suspend, 51562306a36Sopenharmony_ci mxs_spi_runtime_resume, NULL) 51662306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(mxs_spi_suspend, mxs_spi_resume) 51762306a36Sopenharmony_ci}; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic const struct of_device_id mxs_spi_dt_ids[] = { 52062306a36Sopenharmony_ci { .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, }, 52162306a36Sopenharmony_ci { .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, }, 52262306a36Sopenharmony_ci { /* sentinel */ } 52362306a36Sopenharmony_ci}; 52462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxs_spi_dt_ids); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic int mxs_spi_probe(struct platform_device *pdev) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci const struct of_device_id *of_id = 52962306a36Sopenharmony_ci of_match_device(mxs_spi_dt_ids, &pdev->dev); 53062306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 53162306a36Sopenharmony_ci struct spi_master *master; 53262306a36Sopenharmony_ci struct mxs_spi *spi; 53362306a36Sopenharmony_ci struct mxs_ssp *ssp; 53462306a36Sopenharmony_ci struct clk *clk; 53562306a36Sopenharmony_ci void __iomem *base; 53662306a36Sopenharmony_ci int devid, clk_freq; 53762306a36Sopenharmony_ci int ret = 0, irq_err; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* 54062306a36Sopenharmony_ci * Default clock speed for the SPI core. 160MHz seems to 54162306a36Sopenharmony_ci * work reasonably well with most SPI flashes, so use this 54262306a36Sopenharmony_ci * as a default. Override with "clock-frequency" DT prop. 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_ci const int clk_freq_default = 160000000; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci irq_err = platform_get_irq(pdev, 0); 54762306a36Sopenharmony_ci if (irq_err < 0) 54862306a36Sopenharmony_ci return irq_err; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 55162306a36Sopenharmony_ci if (IS_ERR(base)) 55262306a36Sopenharmony_ci return PTR_ERR(base); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci clk = devm_clk_get(&pdev->dev, NULL); 55562306a36Sopenharmony_ci if (IS_ERR(clk)) 55662306a36Sopenharmony_ci return PTR_ERR(clk); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci devid = (enum mxs_ssp_id) of_id->data; 55962306a36Sopenharmony_ci ret = of_property_read_u32(np, "clock-frequency", 56062306a36Sopenharmony_ci &clk_freq); 56162306a36Sopenharmony_ci if (ret) 56262306a36Sopenharmony_ci clk_freq = clk_freq_default; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci master = spi_alloc_master(&pdev->dev, sizeof(*spi)); 56562306a36Sopenharmony_ci if (!master) 56662306a36Sopenharmony_ci return -ENOMEM; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci platform_set_drvdata(pdev, master); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci master->transfer_one_message = mxs_spi_transfer_one; 57162306a36Sopenharmony_ci master->bits_per_word_mask = SPI_BPW_MASK(8); 57262306a36Sopenharmony_ci master->mode_bits = SPI_CPOL | SPI_CPHA; 57362306a36Sopenharmony_ci master->num_chipselect = 3; 57462306a36Sopenharmony_ci master->dev.of_node = np; 57562306a36Sopenharmony_ci master->flags = SPI_CONTROLLER_HALF_DUPLEX; 57662306a36Sopenharmony_ci master->auto_runtime_pm = true; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci spi = spi_master_get_devdata(master); 57962306a36Sopenharmony_ci ssp = &spi->ssp; 58062306a36Sopenharmony_ci ssp->dev = &pdev->dev; 58162306a36Sopenharmony_ci ssp->clk = clk; 58262306a36Sopenharmony_ci ssp->base = base; 58362306a36Sopenharmony_ci ssp->devid = devid; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci init_completion(&spi->c); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0, 58862306a36Sopenharmony_ci dev_name(&pdev->dev), ssp); 58962306a36Sopenharmony_ci if (ret) 59062306a36Sopenharmony_ci goto out_master_free; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ssp->dmach = dma_request_chan(&pdev->dev, "rx-tx"); 59362306a36Sopenharmony_ci if (IS_ERR(ssp->dmach)) { 59462306a36Sopenharmony_ci dev_err(ssp->dev, "Failed to request DMA\n"); 59562306a36Sopenharmony_ci ret = PTR_ERR(ssp->dmach); 59662306a36Sopenharmony_ci goto out_master_free; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci pm_runtime_enable(ssp->dev); 60062306a36Sopenharmony_ci if (!pm_runtime_enabled(ssp->dev)) { 60162306a36Sopenharmony_ci ret = mxs_spi_runtime_resume(ssp->dev); 60262306a36Sopenharmony_ci if (ret < 0) { 60362306a36Sopenharmony_ci dev_err(ssp->dev, "runtime resume failed\n"); 60462306a36Sopenharmony_ci goto out_dma_release; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(ssp->dev); 60962306a36Sopenharmony_ci if (ret < 0) { 61062306a36Sopenharmony_ci dev_err(ssp->dev, "runtime_get_sync failed\n"); 61162306a36Sopenharmony_ci goto out_pm_runtime_disable; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci clk_set_rate(ssp->clk, clk_freq); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci ret = stmp_reset_block(ssp->base); 61762306a36Sopenharmony_ci if (ret) 61862306a36Sopenharmony_ci goto out_pm_runtime_put; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ret = devm_spi_register_master(&pdev->dev, master); 62162306a36Sopenharmony_ci if (ret) { 62262306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret); 62362306a36Sopenharmony_ci goto out_pm_runtime_put; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci pm_runtime_put(ssp->dev); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ciout_pm_runtime_put: 63162306a36Sopenharmony_ci pm_runtime_put(ssp->dev); 63262306a36Sopenharmony_ciout_pm_runtime_disable: 63362306a36Sopenharmony_ci pm_runtime_disable(ssp->dev); 63462306a36Sopenharmony_ciout_dma_release: 63562306a36Sopenharmony_ci dma_release_channel(ssp->dmach); 63662306a36Sopenharmony_ciout_master_free: 63762306a36Sopenharmony_ci spi_master_put(master); 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic void mxs_spi_remove(struct platform_device *pdev) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct spi_master *master; 64462306a36Sopenharmony_ci struct mxs_spi *spi; 64562306a36Sopenharmony_ci struct mxs_ssp *ssp; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci master = platform_get_drvdata(pdev); 64862306a36Sopenharmony_ci spi = spi_master_get_devdata(master); 64962306a36Sopenharmony_ci ssp = &spi->ssp; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 65262306a36Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 65362306a36Sopenharmony_ci mxs_spi_runtime_suspend(&pdev->dev); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci dma_release_channel(ssp->dmach); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic struct platform_driver mxs_spi_driver = { 65962306a36Sopenharmony_ci .probe = mxs_spi_probe, 66062306a36Sopenharmony_ci .remove_new = mxs_spi_remove, 66162306a36Sopenharmony_ci .driver = { 66262306a36Sopenharmony_ci .name = DRIVER_NAME, 66362306a36Sopenharmony_ci .of_match_table = mxs_spi_dt_ids, 66462306a36Sopenharmony_ci .pm = &mxs_spi_pm, 66562306a36Sopenharmony_ci }, 66662306a36Sopenharmony_ci}; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cimodule_platform_driver(mxs_spi_driver); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ciMODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 67162306a36Sopenharmony_ciMODULE_DESCRIPTION("MXS SPI master driver"); 67262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 67362306a36Sopenharmony_ciMODULE_ALIAS("platform:mxs-spi"); 674