1646781d3SMarek Vasut /* 2646781d3SMarek Vasut * Freescale MXS SPI master driver 3646781d3SMarek Vasut * 4646781d3SMarek Vasut * Copyright 2012 DENX Software Engineering, GmbH. 5646781d3SMarek Vasut * Copyright 2012 Freescale Semiconductor, Inc. 6646781d3SMarek Vasut * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. 7646781d3SMarek Vasut * 8646781d3SMarek Vasut * Rework and transition to new API by: 9646781d3SMarek Vasut * Marek Vasut <marex@denx.de> 10646781d3SMarek Vasut * 11646781d3SMarek Vasut * Based on previous attempt by: 12646781d3SMarek Vasut * Fabio Estevam <fabio.estevam@freescale.com> 13646781d3SMarek Vasut * 14646781d3SMarek Vasut * Based on code from U-Boot bootloader by: 15646781d3SMarek Vasut * Marek Vasut <marex@denx.de> 16646781d3SMarek Vasut * 17646781d3SMarek Vasut * Based on spi-stmp.c, which is: 18646781d3SMarek Vasut * Author: Dmitry Pervushin <dimka@embeddedalley.com> 19646781d3SMarek Vasut * 20646781d3SMarek Vasut * This program is free software; you can redistribute it and/or modify 21646781d3SMarek Vasut * it under the terms of the GNU General Public License as published by 22646781d3SMarek Vasut * the Free Software Foundation; either version 2 of the License, or 23646781d3SMarek Vasut * (at your option) any later version. 24646781d3SMarek Vasut * 25646781d3SMarek Vasut * This program is distributed in the hope that it will be useful, 26646781d3SMarek Vasut * but WITHOUT ANY WARRANTY; without even the implied warranty of 27646781d3SMarek Vasut * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28646781d3SMarek Vasut * GNU General Public License for more details. 29646781d3SMarek Vasut */ 30646781d3SMarek Vasut 31646781d3SMarek Vasut #include <linux/kernel.h> 32646781d3SMarek Vasut #include <linux/ioport.h> 33646781d3SMarek Vasut #include <linux/of.h> 34646781d3SMarek Vasut #include <linux/of_device.h> 35646781d3SMarek Vasut #include <linux/of_gpio.h> 36646781d3SMarek Vasut #include <linux/platform_device.h> 37646781d3SMarek Vasut #include <linux/delay.h> 38646781d3SMarek Vasut #include <linux/interrupt.h> 39646781d3SMarek Vasut #include <linux/dma-mapping.h> 40646781d3SMarek Vasut #include <linux/dmaengine.h> 41646781d3SMarek Vasut #include <linux/highmem.h> 42646781d3SMarek Vasut #include <linux/clk.h> 43646781d3SMarek Vasut #include <linux/err.h> 44646781d3SMarek Vasut #include <linux/completion.h> 45646781d3SMarek Vasut #include <linux/gpio.h> 46646781d3SMarek Vasut #include <linux/regulator/consumer.h> 47646781d3SMarek Vasut #include <linux/module.h> 48646781d3SMarek Vasut #include <linux/stmp_device.h> 49646781d3SMarek Vasut #include <linux/spi/spi.h> 50646781d3SMarek Vasut #include <linux/spi/mxs-spi.h> 51646781d3SMarek Vasut 52646781d3SMarek Vasut #define DRIVER_NAME "mxs-spi" 53646781d3SMarek Vasut 54010b4818SMarek Vasut /* Use 10S timeout for very long transfers, it should suffice. */ 55010b4818SMarek Vasut #define SSP_TIMEOUT 10000 56646781d3SMarek Vasut 57474afc04SMarek Vasut #define SG_MAXLEN 0xff00 58474afc04SMarek Vasut 5928cad125STrent Piepho /* 6028cad125STrent Piepho * Flags for txrx functions. More efficient that using an argument register for 6128cad125STrent Piepho * each one. 6228cad125STrent Piepho */ 6328cad125STrent Piepho #define TXRX_WRITE (1<<0) /* This is a write */ 6428cad125STrent Piepho #define TXRX_DEASSERT_CS (1<<1) /* De-assert CS at end of txrx */ 6528cad125STrent Piepho 66646781d3SMarek Vasut struct mxs_spi { 67646781d3SMarek Vasut struct mxs_ssp ssp; 68474afc04SMarek Vasut struct completion c; 69a560943eSTrent Piepho unsigned int sck; /* Rate requested (vs actual) */ 70646781d3SMarek Vasut }; 71646781d3SMarek Vasut 72646781d3SMarek Vasut static int mxs_spi_setup_transfer(struct spi_device *dev, 73aa9e0c6fSTrent Piepho const struct spi_transfer *t) 74646781d3SMarek Vasut { 75646781d3SMarek Vasut struct mxs_spi *spi = spi_master_get_devdata(dev->master); 76646781d3SMarek Vasut struct mxs_ssp *ssp = &spi->ssp; 77aa9e0c6fSTrent Piepho const unsigned int hz = min(dev->max_speed_hz, t->speed_hz); 78646781d3SMarek Vasut 79646781d3SMarek Vasut if (hz == 0) { 80aa9e0c6fSTrent Piepho dev_err(&dev->dev, "SPI clock rate of zero not allowed\n"); 81646781d3SMarek Vasut return -EINVAL; 82646781d3SMarek Vasut } 83646781d3SMarek Vasut 84a560943eSTrent Piepho if (hz != spi->sck) { 85646781d3SMarek Vasut mxs_ssp_set_clk_rate(ssp, hz); 86a560943eSTrent Piepho /* 87a560943eSTrent Piepho * Save requested rate, hz, rather than the actual rate, 88a44619c3SMichael Heimpold * ssp->clk_rate. Otherwise we would set the rate every transfer 89a560943eSTrent Piepho * when the actual rate is not quite the same as requested rate. 90a560943eSTrent Piepho */ 91a560943eSTrent Piepho spi->sck = hz; 92a560943eSTrent Piepho /* 93a560943eSTrent Piepho * Perhaps we should return an error if the actual clock is 94a560943eSTrent Piepho * nowhere close to what was requested? 95a560943eSTrent Piepho */ 96a560943eSTrent Piepho } 97646781d3SMarek Vasut 9858f46e41STrent Piepho writel(BM_SSP_CTRL0_LOCK_CS, 9958f46e41STrent Piepho ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 100646781d3SMarek Vasut 101646781d3SMarek Vasut writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) | 102aa9e0c6fSTrent Piepho BF_SSP_CTRL1_WORD_LENGTH(BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) | 103646781d3SMarek Vasut ((dev->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | 104646781d3SMarek Vasut ((dev->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0), 105646781d3SMarek Vasut ssp->base + HW_SSP_CTRL1(ssp)); 106646781d3SMarek Vasut 107646781d3SMarek Vasut writel(0x0, ssp->base + HW_SSP_CMD0); 108646781d3SMarek Vasut writel(0x0, ssp->base + HW_SSP_CMD1); 109646781d3SMarek Vasut 110646781d3SMarek Vasut return 0; 111646781d3SMarek Vasut } 112646781d3SMarek Vasut 11342e182f8STrent Piepho static u32 mxs_spi_cs_to_reg(unsigned cs) 114646781d3SMarek Vasut { 11542e182f8STrent Piepho u32 select = 0; 116646781d3SMarek Vasut 117646781d3SMarek Vasut /* 118646781d3SMarek Vasut * i.MX28 Datasheet: 17.10.1: HW_SSP_CTRL0 119646781d3SMarek Vasut * 120646781d3SMarek Vasut * The bits BM_SSP_CTRL0_WAIT_FOR_CMD and BM_SSP_CTRL0_WAIT_FOR_IRQ 121646781d3SMarek Vasut * in HW_SSP_CTRL0 register do have multiple usage, please refer to 122646781d3SMarek Vasut * the datasheet for further details. In SPI mode, they are used to 123646781d3SMarek Vasut * toggle the chip-select lines (nCS pins). 124646781d3SMarek Vasut */ 125646781d3SMarek Vasut if (cs & 1) 126646781d3SMarek Vasut select |= BM_SSP_CTRL0_WAIT_FOR_CMD; 127646781d3SMarek Vasut if (cs & 2) 128646781d3SMarek Vasut select |= BM_SSP_CTRL0_WAIT_FOR_IRQ; 129646781d3SMarek Vasut 130646781d3SMarek Vasut return select; 131646781d3SMarek Vasut } 132646781d3SMarek Vasut 133646781d3SMarek Vasut static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set) 134646781d3SMarek Vasut { 135f13639dcSMarek Vasut const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); 136646781d3SMarek Vasut struct mxs_ssp *ssp = &spi->ssp; 13742e182f8STrent Piepho u32 reg; 138646781d3SMarek Vasut 139f13639dcSMarek Vasut do { 140646781d3SMarek Vasut reg = readl_relaxed(ssp->base + offset); 141646781d3SMarek Vasut 142f13639dcSMarek Vasut if (!set) 143f13639dcSMarek Vasut reg = ~reg; 144646781d3SMarek Vasut 145f13639dcSMarek Vasut reg &= mask; 146646781d3SMarek Vasut 147f13639dcSMarek Vasut if (reg == mask) 148646781d3SMarek Vasut return 0; 149f13639dcSMarek Vasut } while (time_before(jiffies, timeout)); 150f13639dcSMarek Vasut 151f13639dcSMarek Vasut return -ETIMEDOUT; 152646781d3SMarek Vasut } 153646781d3SMarek Vasut 154474afc04SMarek Vasut static void mxs_ssp_dma_irq_callback(void *param) 155474afc04SMarek Vasut { 156474afc04SMarek Vasut struct mxs_spi *spi = param; 157a7fa3219SJingoo Han 158474afc04SMarek Vasut complete(&spi->c); 159474afc04SMarek Vasut } 160474afc04SMarek Vasut 161474afc04SMarek Vasut static irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id) 162474afc04SMarek Vasut { 163474afc04SMarek Vasut struct mxs_ssp *ssp = dev_id; 164a7fa3219SJingoo Han 165474afc04SMarek Vasut dev_err(ssp->dev, "%s[%i] CTRL1=%08x STATUS=%08x\n", 166474afc04SMarek Vasut __func__, __LINE__, 167474afc04SMarek Vasut readl(ssp->base + HW_SSP_CTRL1(ssp)), 168474afc04SMarek Vasut readl(ssp->base + HW_SSP_STATUS(ssp))); 169474afc04SMarek Vasut return IRQ_HANDLED; 170474afc04SMarek Vasut } 171474afc04SMarek Vasut 1720b782f70STrent Piepho static int mxs_spi_txrx_dma(struct mxs_spi *spi, 173474afc04SMarek Vasut unsigned char *buf, int len, 17428cad125STrent Piepho unsigned int flags) 175474afc04SMarek Vasut { 176474afc04SMarek Vasut struct mxs_ssp *ssp = &spi->ssp; 177010b4818SMarek Vasut struct dma_async_tx_descriptor *desc = NULL; 178010b4818SMarek Vasut const bool vmalloced_buf = is_vmalloc_addr(buf); 179010b4818SMarek Vasut const int desc_len = vmalloced_buf ? PAGE_SIZE : SG_MAXLEN; 180010b4818SMarek Vasut const int sgs = DIV_ROUND_UP(len, desc_len); 181474afc04SMarek Vasut int sg_count; 182010b4818SMarek Vasut int min, ret; 18342e182f8STrent Piepho u32 ctrl0; 184010b4818SMarek Vasut struct page *vm_page; 185010b4818SMarek Vasut struct { 18642e182f8STrent Piepho u32 pio[4]; 187010b4818SMarek Vasut struct scatterlist sg; 188010b4818SMarek Vasut } *dma_xfer; 189474afc04SMarek Vasut 190010b4818SMarek Vasut if (!len) 191474afc04SMarek Vasut return -EINVAL; 192010b4818SMarek Vasut 193a7fa3219SJingoo Han dma_xfer = kcalloc(sgs, sizeof(*dma_xfer), GFP_KERNEL); 194010b4818SMarek Vasut if (!dma_xfer) 195010b4818SMarek Vasut return -ENOMEM; 196474afc04SMarek Vasut 19716735d02SWolfram Sang reinit_completion(&spi->c); 198474afc04SMarek Vasut 1990b782f70STrent Piepho /* Chip select was already programmed into CTRL0 */ 200010b4818SMarek Vasut ctrl0 = readl(ssp->base + HW_SSP_CTRL0); 201df23286eSTrent Piepho ctrl0 &= ~(BM_SSP_CTRL0_XFER_COUNT | BM_SSP_CTRL0_IGNORE_CRC | 202df23286eSTrent Piepho BM_SSP_CTRL0_READ); 2030b782f70STrent Piepho ctrl0 |= BM_SSP_CTRL0_DATA_XFER; 204010b4818SMarek Vasut 20528cad125STrent Piepho if (!(flags & TXRX_WRITE)) 206010b4818SMarek Vasut ctrl0 |= BM_SSP_CTRL0_READ; 207010b4818SMarek Vasut 208010b4818SMarek Vasut /* Queue the DMA data transfer. */ 209010b4818SMarek Vasut for (sg_count = 0; sg_count < sgs; sg_count++) { 21028cad125STrent Piepho /* Prepare the transfer descriptor. */ 211010b4818SMarek Vasut min = min(len, desc_len); 212010b4818SMarek Vasut 21328cad125STrent Piepho /* 21428cad125STrent Piepho * De-assert CS on last segment if flag is set (i.e., no more 21528cad125STrent Piepho * transfers will follow) 21628cad125STrent Piepho */ 21728cad125STrent Piepho if ((sg_count + 1 == sgs) && (flags & TXRX_DEASSERT_CS)) 218010b4818SMarek Vasut ctrl0 |= BM_SSP_CTRL0_IGNORE_CRC; 219474afc04SMarek Vasut 220ba486a2aSJuha Lumme if (ssp->devid == IMX23_SSP) { 221ba486a2aSJuha Lumme ctrl0 &= ~BM_SSP_CTRL0_XFER_COUNT; 222010b4818SMarek Vasut ctrl0 |= min; 223ba486a2aSJuha Lumme } 224010b4818SMarek Vasut 225010b4818SMarek Vasut dma_xfer[sg_count].pio[0] = ctrl0; 226010b4818SMarek Vasut dma_xfer[sg_count].pio[3] = min; 227010b4818SMarek Vasut 228010b4818SMarek Vasut if (vmalloced_buf) { 229010b4818SMarek Vasut vm_page = vmalloc_to_page(buf); 230010b4818SMarek Vasut if (!vm_page) { 231010b4818SMarek Vasut ret = -ENOMEM; 232010b4818SMarek Vasut goto err_vmalloc; 233010b4818SMarek Vasut } 2349e8987acSCharles Keepax 2359e8987acSCharles Keepax sg_init_table(&dma_xfer[sg_count].sg, 1); 2369e8987acSCharles Keepax sg_set_page(&dma_xfer[sg_count].sg, vm_page, 2379e8987acSCharles Keepax min, offset_in_page(buf)); 238010b4818SMarek Vasut } else { 2399e8987acSCharles Keepax sg_init_one(&dma_xfer[sg_count].sg, buf, min); 240010b4818SMarek Vasut } 241010b4818SMarek Vasut 242010b4818SMarek Vasut ret = dma_map_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, 24328cad125STrent Piepho (flags & TXRX_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); 244010b4818SMarek Vasut 245010b4818SMarek Vasut len -= min; 246010b4818SMarek Vasut buf += min; 247474afc04SMarek Vasut 248474afc04SMarek Vasut /* Queue the PIO register write transfer. */ 249474afc04SMarek Vasut desc = dmaengine_prep_slave_sg(ssp->dmach, 250010b4818SMarek Vasut (struct scatterlist *)dma_xfer[sg_count].pio, 251010b4818SMarek Vasut (ssp->devid == IMX23_SSP) ? 1 : 4, 252010b4818SMarek Vasut DMA_TRANS_NONE, 253010b4818SMarek Vasut sg_count ? DMA_PREP_INTERRUPT : 0); 254474afc04SMarek Vasut if (!desc) { 255474afc04SMarek Vasut dev_err(ssp->dev, 256474afc04SMarek Vasut "Failed to get PIO reg. write descriptor.\n"); 257010b4818SMarek Vasut ret = -EINVAL; 258010b4818SMarek Vasut goto err_mapped; 259474afc04SMarek Vasut } 260474afc04SMarek Vasut 261010b4818SMarek Vasut desc = dmaengine_prep_slave_sg(ssp->dmach, 262010b4818SMarek Vasut &dma_xfer[sg_count].sg, 1, 26328cad125STrent Piepho (flags & TXRX_WRITE) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, 264474afc04SMarek Vasut DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 265474afc04SMarek Vasut 266474afc04SMarek Vasut if (!desc) { 267474afc04SMarek Vasut dev_err(ssp->dev, 268474afc04SMarek Vasut "Failed to get DMA data write descriptor.\n"); 269474afc04SMarek Vasut ret = -EINVAL; 270010b4818SMarek Vasut goto err_mapped; 271010b4818SMarek Vasut } 272474afc04SMarek Vasut } 273474afc04SMarek Vasut 274474afc04SMarek Vasut /* 275474afc04SMarek Vasut * The last descriptor must have this callback, 276474afc04SMarek Vasut * to finish the DMA transaction. 277474afc04SMarek Vasut */ 278474afc04SMarek Vasut desc->callback = mxs_ssp_dma_irq_callback; 279474afc04SMarek Vasut desc->callback_param = spi; 280474afc04SMarek Vasut 281474afc04SMarek Vasut /* Start the transfer. */ 282474afc04SMarek Vasut dmaengine_submit(desc); 283474afc04SMarek Vasut dma_async_issue_pending(ssp->dmach); 284474afc04SMarek Vasut 285474afc04SMarek Vasut ret = wait_for_completion_timeout(&spi->c, 286474afc04SMarek Vasut msecs_to_jiffies(SSP_TIMEOUT)); 287474afc04SMarek Vasut if (!ret) { 288474afc04SMarek Vasut dev_err(ssp->dev, "DMA transfer timeout\n"); 289474afc04SMarek Vasut ret = -ETIMEDOUT; 29044968466SMarek Vasut dmaengine_terminate_all(ssp->dmach); 291010b4818SMarek Vasut goto err_vmalloc; 292474afc04SMarek Vasut } 293474afc04SMarek Vasut 294474afc04SMarek Vasut ret = 0; 295474afc04SMarek Vasut 296010b4818SMarek Vasut err_vmalloc: 297010b4818SMarek Vasut while (--sg_count >= 0) { 298010b4818SMarek Vasut err_mapped: 299010b4818SMarek Vasut dma_unmap_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, 30028cad125STrent Piepho (flags & TXRX_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); 301474afc04SMarek Vasut } 302474afc04SMarek Vasut 303010b4818SMarek Vasut kfree(dma_xfer); 304010b4818SMarek Vasut 305474afc04SMarek Vasut return ret; 306474afc04SMarek Vasut } 307474afc04SMarek Vasut 3080b782f70STrent Piepho static int mxs_spi_txrx_pio(struct mxs_spi *spi, 309646781d3SMarek Vasut unsigned char *buf, int len, 31028cad125STrent Piepho unsigned int flags) 311646781d3SMarek Vasut { 312646781d3SMarek Vasut struct mxs_ssp *ssp = &spi->ssp; 313646781d3SMarek Vasut 314f5bc7384STrent Piepho writel(BM_SSP_CTRL0_IGNORE_CRC, 315f5bc7384STrent Piepho ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); 316646781d3SMarek Vasut 317646781d3SMarek Vasut while (len--) { 31828cad125STrent Piepho if (len == 0 && (flags & TXRX_DEASSERT_CS)) 319f5bc7384STrent Piepho writel(BM_SSP_CTRL0_IGNORE_CRC, 320f5bc7384STrent Piepho ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 321646781d3SMarek Vasut 322646781d3SMarek Vasut if (ssp->devid == IMX23_SSP) { 323646781d3SMarek Vasut writel(BM_SSP_CTRL0_XFER_COUNT, 324646781d3SMarek Vasut ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); 325646781d3SMarek Vasut writel(1, 326646781d3SMarek Vasut ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 327646781d3SMarek Vasut } else { 328646781d3SMarek Vasut writel(1, ssp->base + HW_SSP_XFER_SIZE); 329646781d3SMarek Vasut } 330646781d3SMarek Vasut 33128cad125STrent Piepho if (flags & TXRX_WRITE) 332646781d3SMarek Vasut writel(BM_SSP_CTRL0_READ, 333646781d3SMarek Vasut ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); 334646781d3SMarek Vasut else 335646781d3SMarek Vasut writel(BM_SSP_CTRL0_READ, 336646781d3SMarek Vasut ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 337646781d3SMarek Vasut 338646781d3SMarek Vasut writel(BM_SSP_CTRL0_RUN, 339646781d3SMarek Vasut ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 340646781d3SMarek Vasut 341646781d3SMarek Vasut if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 1)) 342646781d3SMarek Vasut return -ETIMEDOUT; 343646781d3SMarek Vasut 34428cad125STrent Piepho if (flags & TXRX_WRITE) 345646781d3SMarek Vasut writel(*buf, ssp->base + HW_SSP_DATA(ssp)); 346646781d3SMarek Vasut 347646781d3SMarek Vasut writel(BM_SSP_CTRL0_DATA_XFER, 348646781d3SMarek Vasut ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 349646781d3SMarek Vasut 35028cad125STrent Piepho if (!(flags & TXRX_WRITE)) { 351646781d3SMarek Vasut if (mxs_ssp_wait(spi, HW_SSP_STATUS(ssp), 352646781d3SMarek Vasut BM_SSP_STATUS_FIFO_EMPTY, 0)) 353646781d3SMarek Vasut return -ETIMEDOUT; 354646781d3SMarek Vasut 355646781d3SMarek Vasut *buf = (readl(ssp->base + HW_SSP_DATA(ssp)) & 0xff); 356646781d3SMarek Vasut } 357646781d3SMarek Vasut 358646781d3SMarek Vasut if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 0)) 359646781d3SMarek Vasut return -ETIMEDOUT; 360646781d3SMarek Vasut 361646781d3SMarek Vasut buf++; 362646781d3SMarek Vasut } 363646781d3SMarek Vasut 364646781d3SMarek Vasut if (len <= 0) 365646781d3SMarek Vasut return 0; 366646781d3SMarek Vasut 367646781d3SMarek Vasut return -ETIMEDOUT; 368646781d3SMarek Vasut } 369646781d3SMarek Vasut 370646781d3SMarek Vasut static int mxs_spi_transfer_one(struct spi_master *master, 371646781d3SMarek Vasut struct spi_message *m) 372646781d3SMarek Vasut { 373646781d3SMarek Vasut struct mxs_spi *spi = spi_master_get_devdata(master); 374646781d3SMarek Vasut struct mxs_ssp *ssp = &spi->ssp; 3759a7da6ccSAxel Lin struct spi_transfer *t; 37628cad125STrent Piepho unsigned int flag; 377646781d3SMarek Vasut int status = 0; 378646781d3SMarek Vasut 3790b782f70STrent Piepho /* Program CS register bits here, it will be used for all transfers. */ 3800b782f70STrent Piepho writel(BM_SSP_CTRL0_WAIT_FOR_CMD | BM_SSP_CTRL0_WAIT_FOR_IRQ, 3810b782f70STrent Piepho ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); 3820b782f70STrent Piepho writel(mxs_spi_cs_to_reg(m->spi->chip_select), 3830b782f70STrent Piepho ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); 384646781d3SMarek Vasut 3859a7da6ccSAxel Lin list_for_each_entry(t, &m->transfers, transfer_list) { 386646781d3SMarek Vasut 387646781d3SMarek Vasut status = mxs_spi_setup_transfer(m->spi, t); 388646781d3SMarek Vasut if (status) 389646781d3SMarek Vasut break; 390646781d3SMarek Vasut 39128cad125STrent Piepho /* De-assert on last transfer, inverted by cs_change flag */ 39228cad125STrent Piepho flag = (&t->transfer_list == m->transfers.prev) ^ t->cs_change ? 39328cad125STrent Piepho TXRX_DEASSERT_CS : 0; 394646781d3SMarek Vasut 395474afc04SMarek Vasut /* 396474afc04SMarek Vasut * Small blocks can be transfered via PIO. 397474afc04SMarek Vasut * Measured by empiric means: 398474afc04SMarek Vasut * 399474afc04SMarek Vasut * dd if=/dev/mtdblock0 of=/dev/null bs=1024k count=1 400474afc04SMarek Vasut * 401474afc04SMarek Vasut * DMA only: 2.164808 seconds, 473.0KB/s 402474afc04SMarek Vasut * Combined: 1.676276 seconds, 610.9KB/s 403474afc04SMarek Vasut */ 404727c10e3SMarek Vasut if (t->len < 32) { 405474afc04SMarek Vasut writel(BM_SSP_CTRL1_DMA_ENABLE, 406474afc04SMarek Vasut ssp->base + HW_SSP_CTRL1(ssp) + 407474afc04SMarek Vasut STMP_OFFSET_REG_CLR); 408474afc04SMarek Vasut 409646781d3SMarek Vasut if (t->tx_buf) 4100b782f70STrent Piepho status = mxs_spi_txrx_pio(spi, 411474afc04SMarek Vasut (void *)t->tx_buf, 41228cad125STrent Piepho t->len, flag | TXRX_WRITE); 413646781d3SMarek Vasut if (t->rx_buf) 4140b782f70STrent Piepho status = mxs_spi_txrx_pio(spi, 415474afc04SMarek Vasut t->rx_buf, t->len, 41628cad125STrent Piepho flag); 417474afc04SMarek Vasut } else { 418474afc04SMarek Vasut writel(BM_SSP_CTRL1_DMA_ENABLE, 419474afc04SMarek Vasut ssp->base + HW_SSP_CTRL1(ssp) + 420474afc04SMarek Vasut STMP_OFFSET_REG_SET); 421474afc04SMarek Vasut 422474afc04SMarek Vasut if (t->tx_buf) 4230b782f70STrent Piepho status = mxs_spi_txrx_dma(spi, 424474afc04SMarek Vasut (void *)t->tx_buf, t->len, 42528cad125STrent Piepho flag | TXRX_WRITE); 426474afc04SMarek Vasut if (t->rx_buf) 4270b782f70STrent Piepho status = mxs_spi_txrx_dma(spi, 428474afc04SMarek Vasut t->rx_buf, t->len, 42928cad125STrent Piepho flag); 430474afc04SMarek Vasut } 431646781d3SMarek Vasut 432c895db0fSMarek Vasut if (status) { 433c895db0fSMarek Vasut stmp_reset_block(ssp->base); 434646781d3SMarek Vasut break; 435c895db0fSMarek Vasut } 436646781d3SMarek Vasut 437204e706fSMarek Vasut m->actual_length += t->len; 438646781d3SMarek Vasut } 439646781d3SMarek Vasut 440d856f1ebSMarek Vasut m->status = status; 441646781d3SMarek Vasut spi_finalize_current_message(master); 442646781d3SMarek Vasut 443646781d3SMarek Vasut return status; 444646781d3SMarek Vasut } 445646781d3SMarek Vasut 446646781d3SMarek Vasut static const struct of_device_id mxs_spi_dt_ids[] = { 447646781d3SMarek Vasut { .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, }, 448646781d3SMarek Vasut { .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, }, 449646781d3SMarek Vasut { /* sentinel */ } 450646781d3SMarek Vasut }; 451646781d3SMarek Vasut MODULE_DEVICE_TABLE(of, mxs_spi_dt_ids); 452646781d3SMarek Vasut 453fd4a319bSGrant Likely static int mxs_spi_probe(struct platform_device *pdev) 454646781d3SMarek Vasut { 455646781d3SMarek Vasut const struct of_device_id *of_id = 456646781d3SMarek Vasut of_match_device(mxs_spi_dt_ids, &pdev->dev); 457646781d3SMarek Vasut struct device_node *np = pdev->dev.of_node; 458646781d3SMarek Vasut struct spi_master *master; 459646781d3SMarek Vasut struct mxs_spi *spi; 460646781d3SMarek Vasut struct mxs_ssp *ssp; 46126aafa77SShawn Guo struct resource *iores; 462646781d3SMarek Vasut struct clk *clk; 463646781d3SMarek Vasut void __iomem *base; 46426aafa77SShawn Guo int devid, clk_freq; 46526aafa77SShawn Guo int ret = 0, irq_err; 466646781d3SMarek Vasut 467e64d07a2SMarek Vasut /* 468e64d07a2SMarek Vasut * Default clock speed for the SPI core. 160MHz seems to 469e64d07a2SMarek Vasut * work reasonably well with most SPI flashes, so use this 470e64d07a2SMarek Vasut * as a default. Override with "clock-frequency" DT prop. 471e64d07a2SMarek Vasut */ 472e64d07a2SMarek Vasut const int clk_freq_default = 160000000; 473e64d07a2SMarek Vasut 474646781d3SMarek Vasut iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); 475474afc04SMarek Vasut irq_err = platform_get_irq(pdev, 0); 476796305a2SFabio Estevam if (irq_err < 0) 477cdd1945bSFabio Estevam return irq_err; 478646781d3SMarek Vasut 479b0ee5605SThierry Reding base = devm_ioremap_resource(&pdev->dev, iores); 480b0ee5605SThierry Reding if (IS_ERR(base)) 481b0ee5605SThierry Reding return PTR_ERR(base); 482646781d3SMarek Vasut 483646781d3SMarek Vasut clk = devm_clk_get(&pdev->dev, NULL); 484646781d3SMarek Vasut if (IS_ERR(clk)) 485646781d3SMarek Vasut return PTR_ERR(clk); 486646781d3SMarek Vasut 487646781d3SMarek Vasut devid = (enum mxs_ssp_id) of_id->data; 488e64d07a2SMarek Vasut ret = of_property_read_u32(np, "clock-frequency", 489e64d07a2SMarek Vasut &clk_freq); 490e64d07a2SMarek Vasut if (ret) 491e64d07a2SMarek Vasut clk_freq = clk_freq_default; 492646781d3SMarek Vasut 493646781d3SMarek Vasut master = spi_alloc_master(&pdev->dev, sizeof(*spi)); 494646781d3SMarek Vasut if (!master) 495646781d3SMarek Vasut return -ENOMEM; 496646781d3SMarek Vasut 497646781d3SMarek Vasut master->transfer_one_message = mxs_spi_transfer_one; 49824778be2SStephen Warren master->bits_per_word_mask = SPI_BPW_MASK(8); 499646781d3SMarek Vasut master->mode_bits = SPI_CPOL | SPI_CPHA; 500646781d3SMarek Vasut master->num_chipselect = 3; 501646781d3SMarek Vasut master->dev.of_node = np; 502646781d3SMarek Vasut master->flags = SPI_MASTER_HALF_DUPLEX; 503646781d3SMarek Vasut 504646781d3SMarek Vasut spi = spi_master_get_devdata(master); 505646781d3SMarek Vasut ssp = &spi->ssp; 506646781d3SMarek Vasut ssp->dev = &pdev->dev; 507646781d3SMarek Vasut ssp->clk = clk; 508646781d3SMarek Vasut ssp->base = base; 509646781d3SMarek Vasut ssp->devid = devid; 510646781d3SMarek Vasut 51141682e03SMarek Vasut init_completion(&spi->c); 51241682e03SMarek Vasut 513474afc04SMarek Vasut ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0, 514617100c2SFabio Estevam dev_name(&pdev->dev), ssp); 515474afc04SMarek Vasut if (ret) 516474afc04SMarek Vasut goto out_master_free; 517474afc04SMarek Vasut 51826aafa77SShawn Guo ssp->dmach = dma_request_slave_channel(&pdev->dev, "rx-tx"); 519474afc04SMarek Vasut if (!ssp->dmach) { 520474afc04SMarek Vasut dev_err(ssp->dev, "Failed to request DMA\n"); 52158ad60bbSWei Yongjun ret = -ENODEV; 522474afc04SMarek Vasut goto out_master_free; 523474afc04SMarek Vasut } 524474afc04SMarek Vasut 5259c4a39afSFabio Estevam ret = clk_prepare_enable(ssp->clk); 5269c4a39afSFabio Estevam if (ret) 5279c4a39afSFabio Estevam goto out_dma_release; 5289c4a39afSFabio Estevam 529e64d07a2SMarek Vasut clk_set_rate(ssp->clk, clk_freq); 530646781d3SMarek Vasut 5318498bce9SFabio Estevam ret = stmp_reset_block(ssp->base); 5328498bce9SFabio Estevam if (ret) 5338498bce9SFabio Estevam goto out_disable_clk; 534646781d3SMarek Vasut 535646781d3SMarek Vasut platform_set_drvdata(pdev, master); 536646781d3SMarek Vasut 53733e195acSJingoo Han ret = devm_spi_register_master(&pdev->dev, master); 538646781d3SMarek Vasut if (ret) { 539646781d3SMarek Vasut dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret); 5409c4a39afSFabio Estevam goto out_disable_clk; 541646781d3SMarek Vasut } 542646781d3SMarek Vasut 543646781d3SMarek Vasut return 0; 544646781d3SMarek Vasut 5459c4a39afSFabio Estevam out_disable_clk: 546646781d3SMarek Vasut clk_disable_unprepare(ssp->clk); 5479c4a39afSFabio Estevam out_dma_release: 548e11933f6SFabio Estevam dma_release_channel(ssp->dmach); 549474afc04SMarek Vasut out_master_free: 550646781d3SMarek Vasut spi_master_put(master); 551646781d3SMarek Vasut return ret; 552646781d3SMarek Vasut } 553646781d3SMarek Vasut 554fd4a319bSGrant Likely static int mxs_spi_remove(struct platform_device *pdev) 555646781d3SMarek Vasut { 556646781d3SMarek Vasut struct spi_master *master; 557646781d3SMarek Vasut struct mxs_spi *spi; 558646781d3SMarek Vasut struct mxs_ssp *ssp; 559646781d3SMarek Vasut 560*e322ce93SWei Yongjun master = platform_get_drvdata(pdev); 561646781d3SMarek Vasut spi = spi_master_get_devdata(master); 562646781d3SMarek Vasut ssp = &spi->ssp; 563646781d3SMarek Vasut 564646781d3SMarek Vasut clk_disable_unprepare(ssp->clk); 565e11933f6SFabio Estevam dma_release_channel(ssp->dmach); 566646781d3SMarek Vasut 567646781d3SMarek Vasut return 0; 568646781d3SMarek Vasut } 569646781d3SMarek Vasut 570646781d3SMarek Vasut static struct platform_driver mxs_spi_driver = { 571646781d3SMarek Vasut .probe = mxs_spi_probe, 572fd4a319bSGrant Likely .remove = mxs_spi_remove, 573646781d3SMarek Vasut .driver = { 574646781d3SMarek Vasut .name = DRIVER_NAME, 575646781d3SMarek Vasut .of_match_table = mxs_spi_dt_ids, 576646781d3SMarek Vasut }, 577646781d3SMarek Vasut }; 578646781d3SMarek Vasut 579646781d3SMarek Vasut module_platform_driver(mxs_spi_driver); 580646781d3SMarek Vasut 581646781d3SMarek Vasut MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 582646781d3SMarek Vasut MODULE_DESCRIPTION("MXS SPI master driver"); 583646781d3SMarek Vasut MODULE_LICENSE("GPL"); 584646781d3SMarek Vasut MODULE_ALIAS("platform:mxs-spi"); 585