18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// Copyright (c) 2018 MediaTek Inc. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/clk.h> 58c2ecf20Sopenharmony_ci#include <linux/device.h> 68c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 78c2ecf20Sopenharmony_ci#include <linux/err.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 128c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define SPIS_IRQ_EN_REG 0x0 158c2ecf20Sopenharmony_ci#define SPIS_IRQ_CLR_REG 0x4 168c2ecf20Sopenharmony_ci#define SPIS_IRQ_ST_REG 0x8 178c2ecf20Sopenharmony_ci#define SPIS_IRQ_MASK_REG 0xc 188c2ecf20Sopenharmony_ci#define SPIS_CFG_REG 0x10 198c2ecf20Sopenharmony_ci#define SPIS_RX_DATA_REG 0x14 208c2ecf20Sopenharmony_ci#define SPIS_TX_DATA_REG 0x18 218c2ecf20Sopenharmony_ci#define SPIS_RX_DST_REG 0x1c 228c2ecf20Sopenharmony_ci#define SPIS_TX_SRC_REG 0x20 238c2ecf20Sopenharmony_ci#define SPIS_DMA_CFG_REG 0x30 248c2ecf20Sopenharmony_ci#define SPIS_SOFT_RST_REG 0x40 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* SPIS_IRQ_EN_REG */ 278c2ecf20Sopenharmony_ci#define DMA_DONE_EN BIT(7) 288c2ecf20Sopenharmony_ci#define DATA_DONE_EN BIT(2) 298c2ecf20Sopenharmony_ci#define RSTA_DONE_EN BIT(1) 308c2ecf20Sopenharmony_ci#define CMD_INVALID_EN BIT(0) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* SPIS_IRQ_ST_REG */ 338c2ecf20Sopenharmony_ci#define DMA_DONE_ST BIT(7) 348c2ecf20Sopenharmony_ci#define DATA_DONE_ST BIT(2) 358c2ecf20Sopenharmony_ci#define RSTA_DONE_ST BIT(1) 368c2ecf20Sopenharmony_ci#define CMD_INVALID_ST BIT(0) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* SPIS_IRQ_MASK_REG */ 398c2ecf20Sopenharmony_ci#define DMA_DONE_MASK BIT(7) 408c2ecf20Sopenharmony_ci#define DATA_DONE_MASK BIT(2) 418c2ecf20Sopenharmony_ci#define RSTA_DONE_MASK BIT(1) 428c2ecf20Sopenharmony_ci#define CMD_INVALID_MASK BIT(0) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* SPIS_CFG_REG */ 458c2ecf20Sopenharmony_ci#define SPIS_TX_ENDIAN BIT(7) 468c2ecf20Sopenharmony_ci#define SPIS_RX_ENDIAN BIT(6) 478c2ecf20Sopenharmony_ci#define SPIS_TXMSBF BIT(5) 488c2ecf20Sopenharmony_ci#define SPIS_RXMSBF BIT(4) 498c2ecf20Sopenharmony_ci#define SPIS_CPHA BIT(3) 508c2ecf20Sopenharmony_ci#define SPIS_CPOL BIT(2) 518c2ecf20Sopenharmony_ci#define SPIS_TX_EN BIT(1) 528c2ecf20Sopenharmony_ci#define SPIS_RX_EN BIT(0) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* SPIS_DMA_CFG_REG */ 558c2ecf20Sopenharmony_ci#define TX_DMA_TRIG_EN BIT(31) 568c2ecf20Sopenharmony_ci#define TX_DMA_EN BIT(30) 578c2ecf20Sopenharmony_ci#define RX_DMA_EN BIT(29) 588c2ecf20Sopenharmony_ci#define TX_DMA_LEN 0xfffff 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* SPIS_SOFT_RST_REG */ 618c2ecf20Sopenharmony_ci#define SPIS_DMA_ADDR_EN BIT(1) 628c2ecf20Sopenharmony_ci#define SPIS_SOFT_RST BIT(0) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define MTK_SPI_SLAVE_MAX_FIFO_SIZE 512U 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct mtk_spi_slave { 678c2ecf20Sopenharmony_ci struct device *dev; 688c2ecf20Sopenharmony_ci void __iomem *base; 698c2ecf20Sopenharmony_ci struct clk *spi_clk; 708c2ecf20Sopenharmony_ci struct completion xfer_done; 718c2ecf20Sopenharmony_ci struct spi_transfer *cur_transfer; 728c2ecf20Sopenharmony_ci bool slave_aborted; 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic const struct of_device_id mtk_spi_slave_of_match[] = { 768c2ecf20Sopenharmony_ci { .compatible = "mediatek,mt2712-spi-slave", }, 778c2ecf20Sopenharmony_ci {} 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_spi_slave_of_match); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void mtk_spi_slave_disable_dma(struct mtk_spi_slave *mdata) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci u32 reg_val; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci reg_val = readl(mdata->base + SPIS_DMA_CFG_REG); 868c2ecf20Sopenharmony_ci reg_val &= ~RX_DMA_EN; 878c2ecf20Sopenharmony_ci reg_val &= ~TX_DMA_EN; 888c2ecf20Sopenharmony_ci writel(reg_val, mdata->base + SPIS_DMA_CFG_REG); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void mtk_spi_slave_disable_xfer(struct mtk_spi_slave *mdata) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci u32 reg_val; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci reg_val = readl(mdata->base + SPIS_CFG_REG); 968c2ecf20Sopenharmony_ci reg_val &= ~SPIS_TX_EN; 978c2ecf20Sopenharmony_ci reg_val &= ~SPIS_RX_EN; 988c2ecf20Sopenharmony_ci writel(reg_val, mdata->base + SPIS_CFG_REG); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int mtk_spi_slave_wait_for_completion(struct mtk_spi_slave *mdata) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci if (wait_for_completion_interruptible(&mdata->xfer_done) || 1048c2ecf20Sopenharmony_ci mdata->slave_aborted) { 1058c2ecf20Sopenharmony_ci dev_err(mdata->dev, "interrupted\n"); 1068c2ecf20Sopenharmony_ci return -EINTR; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int mtk_spi_slave_prepare_message(struct spi_controller *ctlr, 1138c2ecf20Sopenharmony_ci struct spi_message *msg) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr); 1168c2ecf20Sopenharmony_ci struct spi_device *spi = msg->spi; 1178c2ecf20Sopenharmony_ci bool cpha, cpol; 1188c2ecf20Sopenharmony_ci u32 reg_val; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci cpha = spi->mode & SPI_CPHA ? 1 : 0; 1218c2ecf20Sopenharmony_ci cpol = spi->mode & SPI_CPOL ? 1 : 0; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci reg_val = readl(mdata->base + SPIS_CFG_REG); 1248c2ecf20Sopenharmony_ci if (cpha) 1258c2ecf20Sopenharmony_ci reg_val |= SPIS_CPHA; 1268c2ecf20Sopenharmony_ci else 1278c2ecf20Sopenharmony_ci reg_val &= ~SPIS_CPHA; 1288c2ecf20Sopenharmony_ci if (cpol) 1298c2ecf20Sopenharmony_ci reg_val |= SPIS_CPOL; 1308c2ecf20Sopenharmony_ci else 1318c2ecf20Sopenharmony_ci reg_val &= ~SPIS_CPOL; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (spi->mode & SPI_LSB_FIRST) 1348c2ecf20Sopenharmony_ci reg_val &= ~(SPIS_TXMSBF | SPIS_RXMSBF); 1358c2ecf20Sopenharmony_ci else 1368c2ecf20Sopenharmony_ci reg_val |= SPIS_TXMSBF | SPIS_RXMSBF; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci reg_val &= ~SPIS_TX_ENDIAN; 1398c2ecf20Sopenharmony_ci reg_val &= ~SPIS_RX_ENDIAN; 1408c2ecf20Sopenharmony_ci writel(reg_val, mdata->base + SPIS_CFG_REG); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int mtk_spi_slave_fifo_transfer(struct spi_controller *ctlr, 1468c2ecf20Sopenharmony_ci struct spi_device *spi, 1478c2ecf20Sopenharmony_ci struct spi_transfer *xfer) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr); 1508c2ecf20Sopenharmony_ci int reg_val, cnt, remainder, ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci writel(SPIS_SOFT_RST, mdata->base + SPIS_SOFT_RST_REG); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci reg_val = readl(mdata->base + SPIS_CFG_REG); 1558c2ecf20Sopenharmony_ci if (xfer->rx_buf) 1568c2ecf20Sopenharmony_ci reg_val |= SPIS_RX_EN; 1578c2ecf20Sopenharmony_ci if (xfer->tx_buf) 1588c2ecf20Sopenharmony_ci reg_val |= SPIS_TX_EN; 1598c2ecf20Sopenharmony_ci writel(reg_val, mdata->base + SPIS_CFG_REG); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci cnt = xfer->len / 4; 1628c2ecf20Sopenharmony_ci if (xfer->tx_buf) 1638c2ecf20Sopenharmony_ci iowrite32_rep(mdata->base + SPIS_TX_DATA_REG, 1648c2ecf20Sopenharmony_ci xfer->tx_buf, cnt); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci remainder = xfer->len % 4; 1678c2ecf20Sopenharmony_ci if (xfer->tx_buf && remainder > 0) { 1688c2ecf20Sopenharmony_ci reg_val = 0; 1698c2ecf20Sopenharmony_ci memcpy(®_val, xfer->tx_buf + cnt * 4, remainder); 1708c2ecf20Sopenharmony_ci writel(reg_val, mdata->base + SPIS_TX_DATA_REG); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci ret = mtk_spi_slave_wait_for_completion(mdata); 1748c2ecf20Sopenharmony_ci if (ret) { 1758c2ecf20Sopenharmony_ci mtk_spi_slave_disable_xfer(mdata); 1768c2ecf20Sopenharmony_ci writel(SPIS_SOFT_RST, mdata->base + SPIS_SOFT_RST_REG); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return ret; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int mtk_spi_slave_dma_transfer(struct spi_controller *ctlr, 1838c2ecf20Sopenharmony_ci struct spi_device *spi, 1848c2ecf20Sopenharmony_ci struct spi_transfer *xfer) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr); 1878c2ecf20Sopenharmony_ci struct device *dev = mdata->dev; 1888c2ecf20Sopenharmony_ci int reg_val, ret; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci writel(SPIS_SOFT_RST, mdata->base + SPIS_SOFT_RST_REG); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (xfer->tx_buf) { 1938c2ecf20Sopenharmony_ci /* tx_buf is a const void* where we need a void * for 1948c2ecf20Sopenharmony_ci * the dma mapping 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci void *nonconst_tx = (void *)xfer->tx_buf; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci xfer->tx_dma = dma_map_single(dev, nonconst_tx, 1998c2ecf20Sopenharmony_ci xfer->len, DMA_TO_DEVICE); 2008c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, xfer->tx_dma)) { 2018c2ecf20Sopenharmony_ci ret = -ENOMEM; 2028c2ecf20Sopenharmony_ci goto disable_transfer; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (xfer->rx_buf) { 2078c2ecf20Sopenharmony_ci xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, 2088c2ecf20Sopenharmony_ci xfer->len, DMA_FROM_DEVICE); 2098c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, xfer->rx_dma)) { 2108c2ecf20Sopenharmony_ci ret = -ENOMEM; 2118c2ecf20Sopenharmony_ci goto unmap_txdma; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci writel(xfer->tx_dma, mdata->base + SPIS_TX_SRC_REG); 2168c2ecf20Sopenharmony_ci writel(xfer->rx_dma, mdata->base + SPIS_RX_DST_REG); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci writel(SPIS_DMA_ADDR_EN, mdata->base + SPIS_SOFT_RST_REG); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* enable config reg tx rx_enable */ 2218c2ecf20Sopenharmony_ci reg_val = readl(mdata->base + SPIS_CFG_REG); 2228c2ecf20Sopenharmony_ci if (xfer->tx_buf) 2238c2ecf20Sopenharmony_ci reg_val |= SPIS_TX_EN; 2248c2ecf20Sopenharmony_ci if (xfer->rx_buf) 2258c2ecf20Sopenharmony_ci reg_val |= SPIS_RX_EN; 2268c2ecf20Sopenharmony_ci writel(reg_val, mdata->base + SPIS_CFG_REG); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* config dma */ 2298c2ecf20Sopenharmony_ci reg_val = 0; 2308c2ecf20Sopenharmony_ci reg_val |= (xfer->len - 1) & TX_DMA_LEN; 2318c2ecf20Sopenharmony_ci writel(reg_val, mdata->base + SPIS_DMA_CFG_REG); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci reg_val = readl(mdata->base + SPIS_DMA_CFG_REG); 2348c2ecf20Sopenharmony_ci if (xfer->tx_buf) 2358c2ecf20Sopenharmony_ci reg_val |= TX_DMA_EN; 2368c2ecf20Sopenharmony_ci if (xfer->rx_buf) 2378c2ecf20Sopenharmony_ci reg_val |= RX_DMA_EN; 2388c2ecf20Sopenharmony_ci reg_val |= TX_DMA_TRIG_EN; 2398c2ecf20Sopenharmony_ci writel(reg_val, mdata->base + SPIS_DMA_CFG_REG); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ret = mtk_spi_slave_wait_for_completion(mdata); 2428c2ecf20Sopenharmony_ci if (ret) 2438c2ecf20Sopenharmony_ci goto unmap_rxdma; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ciunmap_rxdma: 2488c2ecf20Sopenharmony_ci if (xfer->rx_buf) 2498c2ecf20Sopenharmony_ci dma_unmap_single(dev, xfer->rx_dma, 2508c2ecf20Sopenharmony_ci xfer->len, DMA_FROM_DEVICE); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ciunmap_txdma: 2538c2ecf20Sopenharmony_ci if (xfer->tx_buf) 2548c2ecf20Sopenharmony_ci dma_unmap_single(dev, xfer->tx_dma, 2558c2ecf20Sopenharmony_ci xfer->len, DMA_TO_DEVICE); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cidisable_transfer: 2588c2ecf20Sopenharmony_ci mtk_spi_slave_disable_dma(mdata); 2598c2ecf20Sopenharmony_ci mtk_spi_slave_disable_xfer(mdata); 2608c2ecf20Sopenharmony_ci writel(SPIS_SOFT_RST, mdata->base + SPIS_SOFT_RST_REG); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int mtk_spi_slave_transfer_one(struct spi_controller *ctlr, 2668c2ecf20Sopenharmony_ci struct spi_device *spi, 2678c2ecf20Sopenharmony_ci struct spi_transfer *xfer) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci reinit_completion(&mdata->xfer_done); 2728c2ecf20Sopenharmony_ci mdata->slave_aborted = false; 2738c2ecf20Sopenharmony_ci mdata->cur_transfer = xfer; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (xfer->len > MTK_SPI_SLAVE_MAX_FIFO_SIZE) 2768c2ecf20Sopenharmony_ci return mtk_spi_slave_dma_transfer(ctlr, spi, xfer); 2778c2ecf20Sopenharmony_ci else 2788c2ecf20Sopenharmony_ci return mtk_spi_slave_fifo_transfer(ctlr, spi, xfer); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int mtk_spi_slave_setup(struct spi_device *spi) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata = spi_controller_get_devdata(spi->master); 2848c2ecf20Sopenharmony_ci u32 reg_val; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci reg_val = DMA_DONE_EN | DATA_DONE_EN | 2878c2ecf20Sopenharmony_ci RSTA_DONE_EN | CMD_INVALID_EN; 2888c2ecf20Sopenharmony_ci writel(reg_val, mdata->base + SPIS_IRQ_EN_REG); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci reg_val = DMA_DONE_MASK | DATA_DONE_MASK | 2918c2ecf20Sopenharmony_ci RSTA_DONE_MASK | CMD_INVALID_MASK; 2928c2ecf20Sopenharmony_ci writel(reg_val, mdata->base + SPIS_IRQ_MASK_REG); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci mtk_spi_slave_disable_dma(mdata); 2958c2ecf20Sopenharmony_ci mtk_spi_slave_disable_xfer(mdata); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic int mtk_slave_abort(struct spi_controller *ctlr) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci mdata->slave_aborted = true; 3058c2ecf20Sopenharmony_ci complete(&mdata->xfer_done); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic irqreturn_t mtk_spi_slave_interrupt(int irq, void *dev_id) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct spi_controller *ctlr = dev_id; 3138c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr); 3148c2ecf20Sopenharmony_ci struct spi_transfer *trans = mdata->cur_transfer; 3158c2ecf20Sopenharmony_ci u32 int_status, reg_val, cnt, remainder; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci int_status = readl(mdata->base + SPIS_IRQ_ST_REG); 3188c2ecf20Sopenharmony_ci writel(int_status, mdata->base + SPIS_IRQ_CLR_REG); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (!trans) 3218c2ecf20Sopenharmony_ci return IRQ_NONE; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if ((int_status & DMA_DONE_ST) && 3248c2ecf20Sopenharmony_ci ((int_status & DATA_DONE_ST) || 3258c2ecf20Sopenharmony_ci (int_status & RSTA_DONE_ST))) { 3268c2ecf20Sopenharmony_ci writel(SPIS_SOFT_RST, mdata->base + SPIS_SOFT_RST_REG); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (trans->tx_buf) 3298c2ecf20Sopenharmony_ci dma_unmap_single(mdata->dev, trans->tx_dma, 3308c2ecf20Sopenharmony_ci trans->len, DMA_TO_DEVICE); 3318c2ecf20Sopenharmony_ci if (trans->rx_buf) 3328c2ecf20Sopenharmony_ci dma_unmap_single(mdata->dev, trans->rx_dma, 3338c2ecf20Sopenharmony_ci trans->len, DMA_FROM_DEVICE); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci mtk_spi_slave_disable_dma(mdata); 3368c2ecf20Sopenharmony_ci mtk_spi_slave_disable_xfer(mdata); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if ((!(int_status & DMA_DONE_ST)) && 3408c2ecf20Sopenharmony_ci ((int_status & DATA_DONE_ST) || 3418c2ecf20Sopenharmony_ci (int_status & RSTA_DONE_ST))) { 3428c2ecf20Sopenharmony_ci cnt = trans->len / 4; 3438c2ecf20Sopenharmony_ci if (trans->rx_buf) 3448c2ecf20Sopenharmony_ci ioread32_rep(mdata->base + SPIS_RX_DATA_REG, 3458c2ecf20Sopenharmony_ci trans->rx_buf, cnt); 3468c2ecf20Sopenharmony_ci remainder = trans->len % 4; 3478c2ecf20Sopenharmony_ci if (trans->rx_buf && remainder > 0) { 3488c2ecf20Sopenharmony_ci reg_val = readl(mdata->base + SPIS_RX_DATA_REG); 3498c2ecf20Sopenharmony_ci memcpy(trans->rx_buf + (cnt * 4), 3508c2ecf20Sopenharmony_ci ®_val, remainder); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci mtk_spi_slave_disable_xfer(mdata); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (int_status & CMD_INVALID_ST) { 3578c2ecf20Sopenharmony_ci dev_warn(&ctlr->dev, "cmd invalid\n"); 3588c2ecf20Sopenharmony_ci return IRQ_NONE; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci mdata->cur_transfer = NULL; 3628c2ecf20Sopenharmony_ci complete(&mdata->xfer_done); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int mtk_spi_slave_probe(struct platform_device *pdev) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct spi_controller *ctlr; 3708c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata; 3718c2ecf20Sopenharmony_ci int irq, ret; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci ctlr = spi_alloc_slave(&pdev->dev, sizeof(*mdata)); 3748c2ecf20Sopenharmony_ci if (!ctlr) { 3758c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to alloc spi slave\n"); 3768c2ecf20Sopenharmony_ci return -ENOMEM; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci ctlr->auto_runtime_pm = true; 3808c2ecf20Sopenharmony_ci ctlr->dev.of_node = pdev->dev.of_node; 3818c2ecf20Sopenharmony_ci ctlr->mode_bits = SPI_CPOL | SPI_CPHA; 3828c2ecf20Sopenharmony_ci ctlr->mode_bits |= SPI_LSB_FIRST; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ctlr->prepare_message = mtk_spi_slave_prepare_message; 3858c2ecf20Sopenharmony_ci ctlr->transfer_one = mtk_spi_slave_transfer_one; 3868c2ecf20Sopenharmony_ci ctlr->setup = mtk_spi_slave_setup; 3878c2ecf20Sopenharmony_ci ctlr->slave_abort = mtk_slave_abort; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci mdata = spi_controller_get_devdata(ctlr); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ctlr); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci init_completion(&mdata->xfer_done); 3948c2ecf20Sopenharmony_ci mdata->dev = &pdev->dev; 3958c2ecf20Sopenharmony_ci mdata->base = devm_platform_ioremap_resource(pdev, 0); 3968c2ecf20Sopenharmony_ci if (IS_ERR(mdata->base)) { 3978c2ecf20Sopenharmony_ci ret = PTR_ERR(mdata->base); 3988c2ecf20Sopenharmony_ci goto err_put_ctlr; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 4028c2ecf20Sopenharmony_ci if (irq < 0) { 4038c2ecf20Sopenharmony_ci ret = irq; 4048c2ecf20Sopenharmony_ci goto err_put_ctlr; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, mtk_spi_slave_interrupt, 4088c2ecf20Sopenharmony_ci IRQF_TRIGGER_NONE, dev_name(&pdev->dev), ctlr); 4098c2ecf20Sopenharmony_ci if (ret) { 4108c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register irq (%d)\n", ret); 4118c2ecf20Sopenharmony_ci goto err_put_ctlr; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci mdata->spi_clk = devm_clk_get(&pdev->dev, "spi"); 4158c2ecf20Sopenharmony_ci if (IS_ERR(mdata->spi_clk)) { 4168c2ecf20Sopenharmony_ci ret = PTR_ERR(mdata->spi_clk); 4178c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret); 4188c2ecf20Sopenharmony_ci goto err_put_ctlr; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ret = clk_prepare_enable(mdata->spi_clk); 4228c2ecf20Sopenharmony_ci if (ret < 0) { 4238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret); 4248c2ecf20Sopenharmony_ci goto err_put_ctlr; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci ret = devm_spi_register_controller(&pdev->dev, ctlr); 4308c2ecf20Sopenharmony_ci if (ret) { 4318c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 4328c2ecf20Sopenharmony_ci "failed to register slave controller(%d)\n", ret); 4338c2ecf20Sopenharmony_ci clk_disable_unprepare(mdata->spi_clk); 4348c2ecf20Sopenharmony_ci goto err_disable_runtime_pm; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci clk_disable_unprepare(mdata->spi_clk); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cierr_disable_runtime_pm: 4428c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 4438c2ecf20Sopenharmony_cierr_put_ctlr: 4448c2ecf20Sopenharmony_ci spi_controller_put(ctlr); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return ret; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int mtk_spi_slave_remove(struct platform_device *pdev) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4578c2ecf20Sopenharmony_cistatic int mtk_spi_slave_suspend(struct device *dev) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct spi_controller *ctlr = dev_get_drvdata(dev); 4608c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr); 4618c2ecf20Sopenharmony_ci int ret; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci ret = spi_controller_suspend(ctlr); 4648c2ecf20Sopenharmony_ci if (ret) 4658c2ecf20Sopenharmony_ci return ret; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(dev)) 4688c2ecf20Sopenharmony_ci clk_disable_unprepare(mdata->spi_clk); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int mtk_spi_slave_resume(struct device *dev) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct spi_controller *ctlr = dev_get_drvdata(dev); 4768c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr); 4778c2ecf20Sopenharmony_ci int ret; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(dev)) { 4808c2ecf20Sopenharmony_ci ret = clk_prepare_enable(mdata->spi_clk); 4818c2ecf20Sopenharmony_ci if (ret < 0) { 4828c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable spi_clk (%d)\n", ret); 4838c2ecf20Sopenharmony_ci return ret; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci ret = spi_controller_resume(ctlr); 4888c2ecf20Sopenharmony_ci if (ret < 0) 4898c2ecf20Sopenharmony_ci clk_disable_unprepare(mdata->spi_clk); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return ret; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4968c2ecf20Sopenharmony_cistatic int mtk_spi_slave_runtime_suspend(struct device *dev) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct spi_controller *ctlr = dev_get_drvdata(dev); 4998c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci clk_disable_unprepare(mdata->spi_clk); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic int mtk_spi_slave_runtime_resume(struct device *dev) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct spi_controller *ctlr = dev_get_drvdata(dev); 5098c2ecf20Sopenharmony_ci struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr); 5108c2ecf20Sopenharmony_ci int ret; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci ret = clk_prepare_enable(mdata->spi_clk); 5138c2ecf20Sopenharmony_ci if (ret < 0) { 5148c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable spi_clk (%d)\n", ret); 5158c2ecf20Sopenharmony_ci return ret; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return 0; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic const struct dev_pm_ops mtk_spi_slave_pm = { 5238c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(mtk_spi_slave_suspend, mtk_spi_slave_resume) 5248c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(mtk_spi_slave_runtime_suspend, 5258c2ecf20Sopenharmony_ci mtk_spi_slave_runtime_resume, NULL) 5268c2ecf20Sopenharmony_ci}; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic struct platform_driver mtk_spi_slave_driver = { 5298c2ecf20Sopenharmony_ci .driver = { 5308c2ecf20Sopenharmony_ci .name = "mtk-spi-slave", 5318c2ecf20Sopenharmony_ci .pm = &mtk_spi_slave_pm, 5328c2ecf20Sopenharmony_ci .of_match_table = mtk_spi_slave_of_match, 5338c2ecf20Sopenharmony_ci }, 5348c2ecf20Sopenharmony_ci .probe = mtk_spi_slave_probe, 5358c2ecf20Sopenharmony_ci .remove = mtk_spi_slave_remove, 5368c2ecf20Sopenharmony_ci}; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cimodule_platform_driver(mtk_spi_slave_driver); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTK SPI Slave Controller driver"); 5418c2ecf20Sopenharmony_ciMODULE_AUTHOR("Leilk Liu <leilk.liu@mediatek.com>"); 5428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5438c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:mtk-spi-slave"); 544