11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a568231fSLeilk Liu /*
3a568231fSLeilk Liu * Copyright (c) 2015 MediaTek Inc.
4a568231fSLeilk Liu * Author: Leilk Liu <leilk.liu@mediatek.com>
5a568231fSLeilk Liu */
6a568231fSLeilk Liu
7a568231fSLeilk Liu #include <linux/clk.h>
8a568231fSLeilk Liu #include <linux/device.h>
9a568231fSLeilk Liu #include <linux/err.h>
10a568231fSLeilk Liu #include <linux/interrupt.h>
11dd69a0a6SLeilk Liu #include <linux/io.h>
12a568231fSLeilk Liu #include <linux/ioport.h>
13a568231fSLeilk Liu #include <linux/module.h>
14a568231fSLeilk Liu #include <linux/of.h>
151a5a87d5SLinus Walleij #include <linux/gpio/consumer.h>
16a568231fSLeilk Liu #include <linux/platform_device.h>
17a568231fSLeilk Liu #include <linux/platform_data/spi-mt65xx.h>
18a568231fSLeilk Liu #include <linux/pm_runtime.h>
19a568231fSLeilk Liu #include <linux/spi/spi.h>
209f763fd2SLeilk Liu #include <linux/spi/spi-mem.h>
21fdeae8f5Sluhua.xu #include <linux/dma-mapping.h>
22a568231fSLeilk Liu
23a568231fSLeilk Liu #define SPI_CFG0_REG 0x0000
24a568231fSLeilk Liu #define SPI_CFG1_REG 0x0004
25a568231fSLeilk Liu #define SPI_TX_SRC_REG 0x0008
26a568231fSLeilk Liu #define SPI_RX_DST_REG 0x000c
27a568231fSLeilk Liu #define SPI_TX_DATA_REG 0x0010
28a568231fSLeilk Liu #define SPI_RX_DATA_REG 0x0014
29a568231fSLeilk Liu #define SPI_CMD_REG 0x0018
30a568231fSLeilk Liu #define SPI_STATUS0_REG 0x001c
31a568231fSLeilk Liu #define SPI_PAD_SEL_REG 0x0024
32058fe49dSLeilk Liu #define SPI_CFG2_REG 0x0028
33fdeae8f5Sluhua.xu #define SPI_TX_SRC_REG_64 0x002c
34fdeae8f5Sluhua.xu #define SPI_RX_DST_REG_64 0x0030
357e963fb2SLeilk Liu #define SPI_CFG3_IPM_REG 0x0040
36a568231fSLeilk Liu
37a568231fSLeilk Liu #define SPI_CFG0_SCK_HIGH_OFFSET 0
38a568231fSLeilk Liu #define SPI_CFG0_SCK_LOW_OFFSET 8
39a568231fSLeilk Liu #define SPI_CFG0_CS_HOLD_OFFSET 16
40a568231fSLeilk Liu #define SPI_CFG0_CS_SETUP_OFFSET 24
41058fe49dSLeilk Liu #define SPI_ADJUST_CFG0_CS_HOLD_OFFSET 0
42058fe49dSLeilk Liu #define SPI_ADJUST_CFG0_CS_SETUP_OFFSET 16
43a568231fSLeilk Liu
44a568231fSLeilk Liu #define SPI_CFG1_CS_IDLE_OFFSET 0
45a568231fSLeilk Liu #define SPI_CFG1_PACKET_LOOP_OFFSET 8
46a568231fSLeilk Liu #define SPI_CFG1_PACKET_LENGTH_OFFSET 16
47f84d866aSMason Zhang #define SPI_CFG1_GET_TICK_DLY_OFFSET 29
4803b1be37SLeilk Liu #define SPI_CFG1_GET_TICK_DLY_OFFSET_V1 30
49a568231fSLeilk Liu
50f84d866aSMason Zhang #define SPI_CFG1_GET_TICK_DLY_MASK 0xe0000000
5103b1be37SLeilk Liu #define SPI_CFG1_GET_TICK_DLY_MASK_V1 0xc0000000
5203b1be37SLeilk Liu
53a568231fSLeilk Liu #define SPI_CFG1_CS_IDLE_MASK 0xff
54a568231fSLeilk Liu #define SPI_CFG1_PACKET_LOOP_MASK 0xff00
55a568231fSLeilk Liu #define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000
567e963fb2SLeilk Liu #define SPI_CFG1_IPM_PACKET_LENGTH_MASK GENMASK(31, 16)
5744b37eb7Sleilk.liu #define SPI_CFG2_SCK_HIGH_OFFSET 0
5844b37eb7Sleilk.liu #define SPI_CFG2_SCK_LOW_OFFSET 16
59a568231fSLeilk Liu
60a71d6ea6SLeilk Liu #define SPI_CMD_ACT BIT(0)
61a71d6ea6SLeilk Liu #define SPI_CMD_RESUME BIT(1)
62a568231fSLeilk Liu #define SPI_CMD_RST BIT(2)
63a568231fSLeilk Liu #define SPI_CMD_PAUSE_EN BIT(4)
64a568231fSLeilk Liu #define SPI_CMD_DEASSERT BIT(5)
65058fe49dSLeilk Liu #define SPI_CMD_SAMPLE_SEL BIT(6)
66058fe49dSLeilk Liu #define SPI_CMD_CS_POL BIT(7)
67a568231fSLeilk Liu #define SPI_CMD_CPHA BIT(8)
68a568231fSLeilk Liu #define SPI_CMD_CPOL BIT(9)
69a568231fSLeilk Liu #define SPI_CMD_RX_DMA BIT(10)
70a568231fSLeilk Liu #define SPI_CMD_TX_DMA BIT(11)
71a568231fSLeilk Liu #define SPI_CMD_TXMSBF BIT(12)
72a568231fSLeilk Liu #define SPI_CMD_RXMSBF BIT(13)
73a568231fSLeilk Liu #define SPI_CMD_RX_ENDIAN BIT(14)
74a568231fSLeilk Liu #define SPI_CMD_TX_ENDIAN BIT(15)
75a568231fSLeilk Liu #define SPI_CMD_FINISH_IE BIT(16)
76a568231fSLeilk Liu #define SPI_CMD_PAUSE_IE BIT(17)
777e963fb2SLeilk Liu #define SPI_CMD_IPM_NONIDLE_MODE BIT(19)
787e963fb2SLeilk Liu #define SPI_CMD_IPM_SPIM_LOOP BIT(21)
797e963fb2SLeilk Liu #define SPI_CMD_IPM_GET_TICKDLY_OFFSET 22
80a568231fSLeilk Liu
817e963fb2SLeilk Liu #define SPI_CMD_IPM_GET_TICKDLY_MASK GENMASK(24, 22)
829f763fd2SLeilk Liu
839f763fd2SLeilk Liu #define PIN_MODE_CFG(x) ((x) / 2)
849f763fd2SLeilk Liu
857e963fb2SLeilk Liu #define SPI_CFG3_IPM_HALF_DUPLEX_DIR BIT(2)
867e963fb2SLeilk Liu #define SPI_CFG3_IPM_HALF_DUPLEX_EN BIT(3)
879f763fd2SLeilk Liu #define SPI_CFG3_IPM_XMODE_EN BIT(4)
889f763fd2SLeilk Liu #define SPI_CFG3_IPM_NODATA_FLAG BIT(5)
899f763fd2SLeilk Liu #define SPI_CFG3_IPM_CMD_BYTELEN_OFFSET 8
909f763fd2SLeilk Liu #define SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET 12
919f763fd2SLeilk Liu
929f763fd2SLeilk Liu #define SPI_CFG3_IPM_CMD_PIN_MODE_MASK GENMASK(1, 0)
939f763fd2SLeilk Liu #define SPI_CFG3_IPM_CMD_BYTELEN_MASK GENMASK(11, 8)
949f763fd2SLeilk Liu #define SPI_CFG3_IPM_ADDR_BYTELEN_MASK GENMASK(15, 12)
959f763fd2SLeilk Liu
96a568231fSLeilk Liu #define MT8173_SPI_MAX_PAD_SEL 3
97a568231fSLeilk Liu
9850f8fec2SLeilk Liu #define MTK_SPI_PAUSE_INT_STATUS 0x2
9950f8fec2SLeilk Liu
1001ce24864SDaniel Kurtz #define MTK_SPI_MAX_FIFO_SIZE 32U
101a568231fSLeilk Liu #define MTK_SPI_PACKET_SIZE 1024
1027e963fb2SLeilk Liu #define MTK_SPI_IPM_PACKET_SIZE SZ_64K
1039f763fd2SLeilk Liu #define MTK_SPI_IPM_PACKET_LOOP SZ_256
1049f763fd2SLeilk Liu
1058e8a9e36SAngeloGioacchino Del Regno #define MTK_SPI_IDLE 0
1068e8a9e36SAngeloGioacchino Del Regno #define MTK_SPI_PAUSED 1
1078e8a9e36SAngeloGioacchino Del Regno
108fdeae8f5Sluhua.xu #define MTK_SPI_32BITS_MASK (0xffffffff)
109fdeae8f5Sluhua.xu
110fdeae8f5Sluhua.xu #define DMA_ADDR_EXT_BITS (36)
111fdeae8f5Sluhua.xu #define DMA_ADDR_DEF_BITS (32)
112a568231fSLeilk Liu
1133c5cd2e2SAngeloGioacchino Del Regno /**
1143c5cd2e2SAngeloGioacchino Del Regno * struct mtk_spi_compatible - device data structure
1153c5cd2e2SAngeloGioacchino Del Regno * @need_pad_sel: Enable pad (pins) selection in SPI controller
1163c5cd2e2SAngeloGioacchino Del Regno * @must_tx: Must explicitly send dummy TX bytes to do RX only transfer
1173c5cd2e2SAngeloGioacchino Del Regno * @enhance_timing: Enable adjusting cfg register to enhance time accuracy
1183c5cd2e2SAngeloGioacchino Del Regno * @dma_ext: DMA address extension supported
1193c5cd2e2SAngeloGioacchino Del Regno * @no_need_unprepare: Don't unprepare the SPI clk during runtime
1203c5cd2e2SAngeloGioacchino Del Regno * @ipm_design: Adjust/extend registers to support IPM design IP features
1213c5cd2e2SAngeloGioacchino Del Regno */
122a568231fSLeilk Liu struct mtk_spi_compatible {
123af57937eSLeilk Liu bool need_pad_sel;
124af57937eSLeilk Liu bool must_tx;
125058fe49dSLeilk Liu bool enhance_timing;
126fdeae8f5Sluhua.xu bool dma_ext;
127162a31efSMason Zhang bool no_need_unprepare;
1287e963fb2SLeilk Liu bool ipm_design;
129a568231fSLeilk Liu };
130a568231fSLeilk Liu
1313c5cd2e2SAngeloGioacchino Del Regno /**
1323c5cd2e2SAngeloGioacchino Del Regno * struct mtk_spi - SPI driver instance
1333c5cd2e2SAngeloGioacchino Del Regno * @base: Start address of the SPI controller registers
1343c5cd2e2SAngeloGioacchino Del Regno * @state: SPI controller state
1353c5cd2e2SAngeloGioacchino Del Regno * @pad_num: Number of pad_sel entries
1363c5cd2e2SAngeloGioacchino Del Regno * @pad_sel: Groups of pins to select
1373c5cd2e2SAngeloGioacchino Del Regno * @parent_clk: Parent of sel_clk
1383c5cd2e2SAngeloGioacchino Del Regno * @sel_clk: SPI master mux clock
1393c5cd2e2SAngeloGioacchino Del Regno * @spi_clk: Peripheral clock
1403c5cd2e2SAngeloGioacchino Del Regno * @spi_hclk: AHB bus clock
1413c5cd2e2SAngeloGioacchino Del Regno * @cur_transfer: Currently processed SPI transfer
1423c5cd2e2SAngeloGioacchino Del Regno * @xfer_len: Number of bytes to transfer
1433c5cd2e2SAngeloGioacchino Del Regno * @num_xfered: Number of transferred bytes
1443c5cd2e2SAngeloGioacchino Del Regno * @tx_sgl: TX transfer scatterlist
1453c5cd2e2SAngeloGioacchino Del Regno * @rx_sgl: RX transfer scatterlist
1463c5cd2e2SAngeloGioacchino Del Regno * @tx_sgl_len: Size of TX DMA transfer
1473c5cd2e2SAngeloGioacchino Del Regno * @rx_sgl_len: Size of RX DMA transfer
1483c5cd2e2SAngeloGioacchino Del Regno * @dev_comp: Device data structure
1493c5cd2e2SAngeloGioacchino Del Regno * @spi_clk_hz: Current SPI clock in Hz
1503c5cd2e2SAngeloGioacchino Del Regno * @spimem_done: SPI-MEM operation completion
1513c5cd2e2SAngeloGioacchino Del Regno * @use_spimem: Enables SPI-MEM
1523c5cd2e2SAngeloGioacchino Del Regno * @dev: Device pointer
1533c5cd2e2SAngeloGioacchino Del Regno * @tx_dma: DMA start for SPI-MEM TX
1543c5cd2e2SAngeloGioacchino Del Regno * @rx_dma: DMA start for SPI-MEM RX
1553c5cd2e2SAngeloGioacchino Del Regno */
156a568231fSLeilk Liu struct mtk_spi {
157a568231fSLeilk Liu void __iomem *base;
158a568231fSLeilk Liu u32 state;
15937457607SLeilk Liu int pad_num;
16037457607SLeilk Liu u32 *pad_sel;
161a740f4e6SLeilk Liu struct clk *parent_clk, *sel_clk, *spi_clk, *spi_hclk;
162a568231fSLeilk Liu struct spi_transfer *cur_transfer;
163a568231fSLeilk Liu u32 xfer_len;
16400bca73bSPeter Shih u32 num_xfered;
165a568231fSLeilk Liu struct scatterlist *tx_sgl, *rx_sgl;
166a568231fSLeilk Liu u32 tx_sgl_len, rx_sgl_len;
167a568231fSLeilk Liu const struct mtk_spi_compatible *dev_comp;
168162a31efSMason Zhang u32 spi_clk_hz;
1699f763fd2SLeilk Liu struct completion spimem_done;
1709f763fd2SLeilk Liu bool use_spimem;
1719f763fd2SLeilk Liu struct device *dev;
1729f763fd2SLeilk Liu dma_addr_t tx_dma;
1739f763fd2SLeilk Liu dma_addr_t rx_dma;
174a568231fSLeilk Liu };
175a568231fSLeilk Liu
1764eaf6f73SLeilk Liu static const struct mtk_spi_compatible mtk_common_compat;
177fc4f226fSLeilk Liu
178b6b1f2d9Sleilk.liu@mediatek.com static const struct mtk_spi_compatible mt2712_compat = {
179b6b1f2d9Sleilk.liu@mediatek.com .must_tx = true,
180b6b1f2d9Sleilk.liu@mediatek.com };
181b6b1f2d9Sleilk.liu@mediatek.com
1827e963fb2SLeilk Liu static const struct mtk_spi_compatible mtk_ipm_compat = {
1837e963fb2SLeilk Liu .enhance_timing = true,
1847e963fb2SLeilk Liu .dma_ext = true,
1857e963fb2SLeilk Liu .ipm_design = true,
1867e963fb2SLeilk Liu };
1877e963fb2SLeilk Liu
1882c231e0aSluhua.xu static const struct mtk_spi_compatible mt6765_compat = {
1892c231e0aSluhua.xu .need_pad_sel = true,
1902c231e0aSluhua.xu .must_tx = true,
1912c231e0aSluhua.xu .enhance_timing = true,
192fdeae8f5Sluhua.xu .dma_ext = true,
1932c231e0aSluhua.xu };
1942c231e0aSluhua.xu
195fc4f226fSLeilk Liu static const struct mtk_spi_compatible mt7622_compat = {
196fc4f226fSLeilk Liu .must_tx = true,
197fc4f226fSLeilk Liu .enhance_timing = true,
198fc4f226fSLeilk Liu };
199fc4f226fSLeilk Liu
200a568231fSLeilk Liu static const struct mtk_spi_compatible mt8173_compat = {
201af57937eSLeilk Liu .need_pad_sel = true,
202af57937eSLeilk Liu .must_tx = true,
203a568231fSLeilk Liu };
204a568231fSLeilk Liu
205b654aa6fSLeilk Liu static const struct mtk_spi_compatible mt8183_compat = {
206b654aa6fSLeilk Liu .need_pad_sel = true,
207b654aa6fSLeilk Liu .must_tx = true,
208b654aa6fSLeilk Liu .enhance_timing = true,
209b654aa6fSLeilk Liu };
210b654aa6fSLeilk Liu
211162a31efSMason Zhang static const struct mtk_spi_compatible mt6893_compat = {
212162a31efSMason Zhang .need_pad_sel = true,
213162a31efSMason Zhang .must_tx = true,
214162a31efSMason Zhang .enhance_timing = true,
215162a31efSMason Zhang .dma_ext = true,
216162a31efSMason Zhang .no_need_unprepare = true,
217162a31efSMason Zhang };
218162a31efSMason Zhang
219a568231fSLeilk Liu /*
220a568231fSLeilk Liu * A piece of default chip info unless the platform
221a568231fSLeilk Liu * supplies it.
222a568231fSLeilk Liu */
223a568231fSLeilk Liu static const struct mtk_chip_config mtk_default_chip_info = {
224058fe49dSLeilk Liu .sample_sel = 0,
225f84d866aSMason Zhang .tick_delay = 0,
226a568231fSLeilk Liu };
227a568231fSLeilk Liu
228a568231fSLeilk Liu static const struct of_device_id mtk_spi_of_match[] = {
2297e963fb2SLeilk Liu { .compatible = "mediatek,spi-ipm",
2307e963fb2SLeilk Liu .data = (void *)&mtk_ipm_compat,
2317e963fb2SLeilk Liu },
23215bcdefdSLeilk Liu { .compatible = "mediatek,mt2701-spi",
23315bcdefdSLeilk Liu .data = (void *)&mtk_common_compat,
23415bcdefdSLeilk Liu },
235b6b1f2d9Sleilk.liu@mediatek.com { .compatible = "mediatek,mt2712-spi",
236b6b1f2d9Sleilk.liu@mediatek.com .data = (void *)&mt2712_compat,
237b6b1f2d9Sleilk.liu@mediatek.com },
2384eaf6f73SLeilk Liu { .compatible = "mediatek,mt6589-spi",
2394eaf6f73SLeilk Liu .data = (void *)&mtk_common_compat,
2404eaf6f73SLeilk Liu },
2412c231e0aSluhua.xu { .compatible = "mediatek,mt6765-spi",
2422c231e0aSluhua.xu .data = (void *)&mt6765_compat,
2432c231e0aSluhua.xu },
244fc4f226fSLeilk Liu { .compatible = "mediatek,mt7622-spi",
245fc4f226fSLeilk Liu .data = (void *)&mt7622_compat,
246fc4f226fSLeilk Liu },
247942779c6SLeilk Liu { .compatible = "mediatek,mt7629-spi",
248942779c6SLeilk Liu .data = (void *)&mt7622_compat,
249942779c6SLeilk Liu },
2504eaf6f73SLeilk Liu { .compatible = "mediatek,mt8135-spi",
2514eaf6f73SLeilk Liu .data = (void *)&mtk_common_compat,
2524eaf6f73SLeilk Liu },
2534eaf6f73SLeilk Liu { .compatible = "mediatek,mt8173-spi",
2544eaf6f73SLeilk Liu .data = (void *)&mt8173_compat,
2554eaf6f73SLeilk Liu },
256b654aa6fSLeilk Liu { .compatible = "mediatek,mt8183-spi",
257b654aa6fSLeilk Liu .data = (void *)&mt8183_compat,
258b654aa6fSLeilk Liu },
2598cf125c4Sleilk.liu { .compatible = "mediatek,mt8192-spi",
2608cf125c4Sleilk.liu .data = (void *)&mt6765_compat,
2618cf125c4Sleilk.liu },
262162a31efSMason Zhang { .compatible = "mediatek,mt6893-spi",
263162a31efSMason Zhang .data = (void *)&mt6893_compat,
264162a31efSMason Zhang },
265a568231fSLeilk Liu {}
266a568231fSLeilk Liu };
267a568231fSLeilk Liu MODULE_DEVICE_TABLE(of, mtk_spi_of_match);
268a568231fSLeilk Liu
mtk_spi_reset(struct mtk_spi * mdata)269a568231fSLeilk Liu static void mtk_spi_reset(struct mtk_spi *mdata)
270a568231fSLeilk Liu {
271a568231fSLeilk Liu u32 reg_val;
272a568231fSLeilk Liu
273a568231fSLeilk Liu /* set the software reset bit in SPI_CMD_REG. */
274a568231fSLeilk Liu reg_val = readl(mdata->base + SPI_CMD_REG);
275a568231fSLeilk Liu reg_val |= SPI_CMD_RST;
276a568231fSLeilk Liu writel(reg_val, mdata->base + SPI_CMD_REG);
277a568231fSLeilk Liu
278a568231fSLeilk Liu reg_val = readl(mdata->base + SPI_CMD_REG);
279a568231fSLeilk Liu reg_val &= ~SPI_CMD_RST;
280a568231fSLeilk Liu writel(reg_val, mdata->base + SPI_CMD_REG);
281a568231fSLeilk Liu }
282a568231fSLeilk Liu
mtk_spi_set_hw_cs_timing(struct spi_device * spi)28304e6bb0dSMason Zhang static int mtk_spi_set_hw_cs_timing(struct spi_device *spi)
28404e6bb0dSMason Zhang {
28504e6bb0dSMason Zhang struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
28604e6bb0dSMason Zhang struct spi_delay *cs_setup = &spi->cs_setup;
28704e6bb0dSMason Zhang struct spi_delay *cs_hold = &spi->cs_hold;
28804e6bb0dSMason Zhang struct spi_delay *cs_inactive = &spi->cs_inactive;
2895c842e51SMason Zhang u32 setup, hold, inactive;
29004e6bb0dSMason Zhang u32 reg_val;
29104e6bb0dSMason Zhang int delay;
29204e6bb0dSMason Zhang
29304e6bb0dSMason Zhang delay = spi_delay_to_ns(cs_setup, NULL);
29404e6bb0dSMason Zhang if (delay < 0)
29504e6bb0dSMason Zhang return delay;
29604e6bb0dSMason Zhang setup = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000;
29704e6bb0dSMason Zhang
29804e6bb0dSMason Zhang delay = spi_delay_to_ns(cs_hold, NULL);
29904e6bb0dSMason Zhang if (delay < 0)
30004e6bb0dSMason Zhang return delay;
30104e6bb0dSMason Zhang hold = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000;
30204e6bb0dSMason Zhang
30304e6bb0dSMason Zhang delay = spi_delay_to_ns(cs_inactive, NULL);
30404e6bb0dSMason Zhang if (delay < 0)
30504e6bb0dSMason Zhang return delay;
30604e6bb0dSMason Zhang inactive = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000;
30704e6bb0dSMason Zhang
3083672bb82SDafna Hirschfeld if (hold || setup) {
30904e6bb0dSMason Zhang reg_val = readl(mdata->base + SPI_CFG0_REG);
31004e6bb0dSMason Zhang if (mdata->dev_comp->enhance_timing) {
3113672bb82SDafna Hirschfeld if (hold) {
3125c842e51SMason Zhang hold = min_t(u32, hold, 0x10000);
31304e6bb0dSMason Zhang reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
31404e6bb0dSMason Zhang reg_val |= (((hold - 1) & 0xffff)
31504e6bb0dSMason Zhang << SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
3163672bb82SDafna Hirschfeld }
3173672bb82SDafna Hirschfeld if (setup) {
3183672bb82SDafna Hirschfeld setup = min_t(u32, setup, 0x10000);
31904e6bb0dSMason Zhang reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
32004e6bb0dSMason Zhang reg_val |= (((setup - 1) & 0xffff)
32104e6bb0dSMason Zhang << SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
3223672bb82SDafna Hirschfeld }
32304e6bb0dSMason Zhang } else {
3243672bb82SDafna Hirschfeld if (hold) {
3255c842e51SMason Zhang hold = min_t(u32, hold, 0x100);
32604e6bb0dSMason Zhang reg_val &= ~(0xff << SPI_CFG0_CS_HOLD_OFFSET);
32704e6bb0dSMason Zhang reg_val |= (((hold - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET);
3283672bb82SDafna Hirschfeld }
3293672bb82SDafna Hirschfeld if (setup) {
3303672bb82SDafna Hirschfeld setup = min_t(u32, setup, 0x100);
33104e6bb0dSMason Zhang reg_val &= ~(0xff << SPI_CFG0_CS_SETUP_OFFSET);
33204e6bb0dSMason Zhang reg_val |= (((setup - 1) & 0xff)
33304e6bb0dSMason Zhang << SPI_CFG0_CS_SETUP_OFFSET);
33404e6bb0dSMason Zhang }
3353672bb82SDafna Hirschfeld }
33604e6bb0dSMason Zhang writel(reg_val, mdata->base + SPI_CFG0_REG);
3373672bb82SDafna Hirschfeld }
33804e6bb0dSMason Zhang
3393672bb82SDafna Hirschfeld if (inactive) {
3405c842e51SMason Zhang inactive = min_t(u32, inactive, 0x100);
34104e6bb0dSMason Zhang reg_val = readl(mdata->base + SPI_CFG1_REG);
34204e6bb0dSMason Zhang reg_val &= ~SPI_CFG1_CS_IDLE_MASK;
34304e6bb0dSMason Zhang reg_val |= (((inactive - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET);
34404e6bb0dSMason Zhang writel(reg_val, mdata->base + SPI_CFG1_REG);
3453672bb82SDafna Hirschfeld }
34604e6bb0dSMason Zhang
34704e6bb0dSMason Zhang return 0;
34804e6bb0dSMason Zhang }
34904e6bb0dSMason Zhang
mtk_spi_hw_init(struct spi_master * master,struct spi_device * spi)3507e963fb2SLeilk Liu static int mtk_spi_hw_init(struct spi_master *master,
3517e963fb2SLeilk Liu struct spi_device *spi)
352a568231fSLeilk Liu {
35379b5d3f2SLeilk Liu u16 cpha, cpol;
354a568231fSLeilk Liu u32 reg_val;
35558a984c7SLeilk Liu struct mtk_chip_config *chip_config = spi->controller_data;
35679b5d3f2SLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
35779b5d3f2SLeilk Liu
35879b5d3f2SLeilk Liu cpha = spi->mode & SPI_CPHA ? 1 : 0;
35979b5d3f2SLeilk Liu cpol = spi->mode & SPI_CPOL ? 1 : 0;
36079b5d3f2SLeilk Liu
36179b5d3f2SLeilk Liu reg_val = readl(mdata->base + SPI_CMD_REG);
3627e963fb2SLeilk Liu if (mdata->dev_comp->ipm_design) {
3637e963fb2SLeilk Liu /* SPI transfer without idle time until packet length done */
3647e963fb2SLeilk Liu reg_val |= SPI_CMD_IPM_NONIDLE_MODE;
3657e963fb2SLeilk Liu if (spi->mode & SPI_LOOP)
3667e963fb2SLeilk Liu reg_val |= SPI_CMD_IPM_SPIM_LOOP;
3677e963fb2SLeilk Liu else
3687e963fb2SLeilk Liu reg_val &= ~SPI_CMD_IPM_SPIM_LOOP;
3697e963fb2SLeilk Liu }
3707e963fb2SLeilk Liu
37179b5d3f2SLeilk Liu if (cpha)
37279b5d3f2SLeilk Liu reg_val |= SPI_CMD_CPHA;
37379b5d3f2SLeilk Liu else
37479b5d3f2SLeilk Liu reg_val &= ~SPI_CMD_CPHA;
37579b5d3f2SLeilk Liu if (cpol)
37679b5d3f2SLeilk Liu reg_val |= SPI_CMD_CPOL;
37779b5d3f2SLeilk Liu else
37879b5d3f2SLeilk Liu reg_val &= ~SPI_CMD_CPOL;
379a568231fSLeilk Liu
380a568231fSLeilk Liu /* set the mlsbx and mlsbtx */
3813e582c6eSLeilk Liu if (spi->mode & SPI_LSB_FIRST) {
382a71d6ea6SLeilk Liu reg_val &= ~SPI_CMD_TXMSBF;
383a71d6ea6SLeilk Liu reg_val &= ~SPI_CMD_RXMSBF;
3843e582c6eSLeilk Liu } else {
3853e582c6eSLeilk Liu reg_val |= SPI_CMD_TXMSBF;
3863e582c6eSLeilk Liu reg_val |= SPI_CMD_RXMSBF;
3873e582c6eSLeilk Liu }
388a568231fSLeilk Liu
389a568231fSLeilk Liu /* set the tx/rx endian */
39044f636daSLeilk Liu #ifdef __LITTLE_ENDIAN
39144f636daSLeilk Liu reg_val &= ~SPI_CMD_TX_ENDIAN;
39244f636daSLeilk Liu reg_val &= ~SPI_CMD_RX_ENDIAN;
39344f636daSLeilk Liu #else
39444f636daSLeilk Liu reg_val |= SPI_CMD_TX_ENDIAN;
39544f636daSLeilk Liu reg_val |= SPI_CMD_RX_ENDIAN;
39644f636daSLeilk Liu #endif
397a568231fSLeilk Liu
398058fe49dSLeilk Liu if (mdata->dev_comp->enhance_timing) {
399ae7c2d34SLuhua Xu /* set CS polarity */
400ae7c2d34SLuhua Xu if (spi->mode & SPI_CS_HIGH)
401058fe49dSLeilk Liu reg_val |= SPI_CMD_CS_POL;
402058fe49dSLeilk Liu else
403058fe49dSLeilk Liu reg_val &= ~SPI_CMD_CS_POL;
404ae7c2d34SLuhua Xu
405058fe49dSLeilk Liu if (chip_config->sample_sel)
406058fe49dSLeilk Liu reg_val |= SPI_CMD_SAMPLE_SEL;
407058fe49dSLeilk Liu else
408058fe49dSLeilk Liu reg_val &= ~SPI_CMD_SAMPLE_SEL;
409058fe49dSLeilk Liu }
410058fe49dSLeilk Liu
411a568231fSLeilk Liu /* set finish and pause interrupt always enable */
41215293324SLeilk Liu reg_val |= SPI_CMD_FINISH_IE | SPI_CMD_PAUSE_IE;
413a568231fSLeilk Liu
414a568231fSLeilk Liu /* disable dma mode */
415a568231fSLeilk Liu reg_val &= ~(SPI_CMD_TX_DMA | SPI_CMD_RX_DMA);
416a568231fSLeilk Liu
417a568231fSLeilk Liu /* disable deassert mode */
418a568231fSLeilk Liu reg_val &= ~SPI_CMD_DEASSERT;
419a568231fSLeilk Liu
420a568231fSLeilk Liu writel(reg_val, mdata->base + SPI_CMD_REG);
421a568231fSLeilk Liu
422a568231fSLeilk Liu /* pad select */
423a568231fSLeilk Liu if (mdata->dev_comp->need_pad_sel)
4249e264f3fSAmit Kumar Mahapatra via Alsa-devel writel(mdata->pad_sel[spi_get_chipselect(spi, 0)],
42537457607SLeilk Liu mdata->base + SPI_PAD_SEL_REG);
426a568231fSLeilk Liu
427f84d866aSMason Zhang /* tick delay */
42803b1be37SLeilk Liu if (mdata->dev_comp->enhance_timing) {
4297e963fb2SLeilk Liu if (mdata->dev_comp->ipm_design) {
4307e963fb2SLeilk Liu reg_val = readl(mdata->base + SPI_CMD_REG);
4317e963fb2SLeilk Liu reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK;
4327e963fb2SLeilk Liu reg_val |= ((chip_config->tick_delay & 0x7)
4337e963fb2SLeilk Liu << SPI_CMD_IPM_GET_TICKDLY_OFFSET);
4347e963fb2SLeilk Liu writel(reg_val, mdata->base + SPI_CMD_REG);
4357e963fb2SLeilk Liu } else {
4367e963fb2SLeilk Liu reg_val = readl(mdata->base + SPI_CFG1_REG);
437f84d866aSMason Zhang reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK;
438f84d866aSMason Zhang reg_val |= ((chip_config->tick_delay & 0x7)
439f84d866aSMason Zhang << SPI_CFG1_GET_TICK_DLY_OFFSET);
4407e963fb2SLeilk Liu writel(reg_val, mdata->base + SPI_CFG1_REG);
4417e963fb2SLeilk Liu }
44203b1be37SLeilk Liu } else {
4437e963fb2SLeilk Liu reg_val = readl(mdata->base + SPI_CFG1_REG);
44403b1be37SLeilk Liu reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK_V1;
44503b1be37SLeilk Liu reg_val |= ((chip_config->tick_delay & 0x3)
44603b1be37SLeilk Liu << SPI_CFG1_GET_TICK_DLY_OFFSET_V1);
447f84d866aSMason Zhang writel(reg_val, mdata->base + SPI_CFG1_REG);
4487e963fb2SLeilk Liu }
449f84d866aSMason Zhang
45004e6bb0dSMason Zhang /* set hw cs timing */
45104e6bb0dSMason Zhang mtk_spi_set_hw_cs_timing(spi);
452a568231fSLeilk Liu return 0;
453a568231fSLeilk Liu }
454a568231fSLeilk Liu
mtk_spi_prepare_message(struct spi_master * master,struct spi_message * msg)4557e963fb2SLeilk Liu static int mtk_spi_prepare_message(struct spi_master *master,
4567e963fb2SLeilk Liu struct spi_message *msg)
4577e963fb2SLeilk Liu {
4587e963fb2SLeilk Liu return mtk_spi_hw_init(master, msg->spi);
4597e963fb2SLeilk Liu }
4607e963fb2SLeilk Liu
mtk_spi_set_cs(struct spi_device * spi,bool enable)461a568231fSLeilk Liu static void mtk_spi_set_cs(struct spi_device *spi, bool enable)
462a568231fSLeilk Liu {
463a568231fSLeilk Liu u32 reg_val;
464a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
465a568231fSLeilk Liu
466ae7c2d34SLuhua Xu if (spi->mode & SPI_CS_HIGH)
467ae7c2d34SLuhua Xu enable = !enable;
468ae7c2d34SLuhua Xu
469a568231fSLeilk Liu reg_val = readl(mdata->base + SPI_CMD_REG);
4706583d203SLeilk Liu if (!enable) {
471a568231fSLeilk Liu reg_val |= SPI_CMD_PAUSE_EN;
4726583d203SLeilk Liu writel(reg_val, mdata->base + SPI_CMD_REG);
4736583d203SLeilk Liu } else {
474a568231fSLeilk Liu reg_val &= ~SPI_CMD_PAUSE_EN;
475a568231fSLeilk Liu writel(reg_val, mdata->base + SPI_CMD_REG);
4766583d203SLeilk Liu mdata->state = MTK_SPI_IDLE;
4776583d203SLeilk Liu mtk_spi_reset(mdata);
4786583d203SLeilk Liu }
479a568231fSLeilk Liu }
480a568231fSLeilk Liu
mtk_spi_prepare_transfer(struct spi_master * master,u32 speed_hz)481a568231fSLeilk Liu static void mtk_spi_prepare_transfer(struct spi_master *master,
4827e963fb2SLeilk Liu u32 speed_hz)
483a568231fSLeilk Liu {
484162a31efSMason Zhang u32 div, sck_time, reg_val;
485a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
486a568231fSLeilk Liu
4877e963fb2SLeilk Liu if (speed_hz < mdata->spi_clk_hz / 2)
4887e963fb2SLeilk Liu div = DIV_ROUND_UP(mdata->spi_clk_hz, speed_hz);
489a568231fSLeilk Liu else
490a568231fSLeilk Liu div = 1;
491a568231fSLeilk Liu
4922ce0acf5SLeilk Liu sck_time = (div + 1) / 2;
493a568231fSLeilk Liu
494058fe49dSLeilk Liu if (mdata->dev_comp->enhance_timing) {
4959f6e7e8dSleilk.liu reg_val = readl(mdata->base + SPI_CFG2_REG);
4969f6e7e8dSleilk.liu reg_val &= ~(0xffff << SPI_CFG2_SCK_HIGH_OFFSET);
4979f6e7e8dSleilk.liu reg_val |= (((sck_time - 1) & 0xffff)
49844b37eb7Sleilk.liu << SPI_CFG2_SCK_HIGH_OFFSET);
4999f6e7e8dSleilk.liu reg_val &= ~(0xffff << SPI_CFG2_SCK_LOW_OFFSET);
500058fe49dSLeilk Liu reg_val |= (((sck_time - 1) & 0xffff)
50144b37eb7Sleilk.liu << SPI_CFG2_SCK_LOW_OFFSET);
502058fe49dSLeilk Liu writel(reg_val, mdata->base + SPI_CFG2_REG);
503058fe49dSLeilk Liu } else {
5049f6e7e8dSleilk.liu reg_val = readl(mdata->base + SPI_CFG0_REG);
5059f6e7e8dSleilk.liu reg_val &= ~(0xff << SPI_CFG0_SCK_HIGH_OFFSET);
5069f6e7e8dSleilk.liu reg_val |= (((sck_time - 1) & 0xff)
507058fe49dSLeilk Liu << SPI_CFG0_SCK_HIGH_OFFSET);
5089f6e7e8dSleilk.liu reg_val &= ~(0xff << SPI_CFG0_SCK_LOW_OFFSET);
5092ce0acf5SLeilk Liu reg_val |= (((sck_time - 1) & 0xff) << SPI_CFG0_SCK_LOW_OFFSET);
510a568231fSLeilk Liu writel(reg_val, mdata->base + SPI_CFG0_REG);
511058fe49dSLeilk Liu }
512a568231fSLeilk Liu }
513a568231fSLeilk Liu
mtk_spi_setup_packet(struct spi_master * master)514a568231fSLeilk Liu static void mtk_spi_setup_packet(struct spi_master *master)
515a568231fSLeilk Liu {
516a568231fSLeilk Liu u32 packet_size, packet_loop, reg_val;
517a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
518a568231fSLeilk Liu
5197e963fb2SLeilk Liu if (mdata->dev_comp->ipm_design)
5207e963fb2SLeilk Liu packet_size = min_t(u32,
5217e963fb2SLeilk Liu mdata->xfer_len,
5227e963fb2SLeilk Liu MTK_SPI_IPM_PACKET_SIZE);
5237e963fb2SLeilk Liu else
5247e963fb2SLeilk Liu packet_size = min_t(u32,
5257e963fb2SLeilk Liu mdata->xfer_len,
5267e963fb2SLeilk Liu MTK_SPI_PACKET_SIZE);
5277e963fb2SLeilk Liu
528a568231fSLeilk Liu packet_loop = mdata->xfer_len / packet_size;
529a568231fSLeilk Liu
530a568231fSLeilk Liu reg_val = readl(mdata->base + SPI_CFG1_REG);
5317e963fb2SLeilk Liu if (mdata->dev_comp->ipm_design)
5327e963fb2SLeilk Liu reg_val &= ~SPI_CFG1_IPM_PACKET_LENGTH_MASK;
5337e963fb2SLeilk Liu else
5347e963fb2SLeilk Liu reg_val &= ~SPI_CFG1_PACKET_LENGTH_MASK;
535a568231fSLeilk Liu reg_val |= (packet_size - 1) << SPI_CFG1_PACKET_LENGTH_OFFSET;
5367e963fb2SLeilk Liu reg_val &= ~SPI_CFG1_PACKET_LOOP_MASK;
537a568231fSLeilk Liu reg_val |= (packet_loop - 1) << SPI_CFG1_PACKET_LOOP_OFFSET;
538a568231fSLeilk Liu writel(reg_val, mdata->base + SPI_CFG1_REG);
539a568231fSLeilk Liu }
540a568231fSLeilk Liu
mtk_spi_enable_transfer(struct spi_master * master)541a568231fSLeilk Liu static void mtk_spi_enable_transfer(struct spi_master *master)
542a568231fSLeilk Liu {
54350f8fec2SLeilk Liu u32 cmd;
544a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
545a568231fSLeilk Liu
546a568231fSLeilk Liu cmd = readl(mdata->base + SPI_CMD_REG);
547a568231fSLeilk Liu if (mdata->state == MTK_SPI_IDLE)
548a71d6ea6SLeilk Liu cmd |= SPI_CMD_ACT;
549a568231fSLeilk Liu else
550a71d6ea6SLeilk Liu cmd |= SPI_CMD_RESUME;
551a568231fSLeilk Liu writel(cmd, mdata->base + SPI_CMD_REG);
552a568231fSLeilk Liu }
553a568231fSLeilk Liu
mtk_spi_get_mult_delta(struct mtk_spi * mdata,u32 xfer_len)554cf82d0ecSzhichao.liu static int mtk_spi_get_mult_delta(struct mtk_spi *mdata, u32 xfer_len)
555a568231fSLeilk Liu {
556cf82d0ecSzhichao.liu u32 mult_delta = 0;
557a568231fSLeilk Liu
558cf82d0ecSzhichao.liu if (mdata->dev_comp->ipm_design) {
559cf82d0ecSzhichao.liu if (xfer_len > MTK_SPI_IPM_PACKET_SIZE)
560cf82d0ecSzhichao.liu mult_delta = xfer_len % MTK_SPI_IPM_PACKET_SIZE;
561cf82d0ecSzhichao.liu } else {
562a568231fSLeilk Liu if (xfer_len > MTK_SPI_PACKET_SIZE)
563a568231fSLeilk Liu mult_delta = xfer_len % MTK_SPI_PACKET_SIZE;
564cf82d0ecSzhichao.liu }
565a568231fSLeilk Liu
566a568231fSLeilk Liu return mult_delta;
567a568231fSLeilk Liu }
568a568231fSLeilk Liu
mtk_spi_update_mdata_len(struct spi_master * master)569a568231fSLeilk Liu static void mtk_spi_update_mdata_len(struct spi_master *master)
570a568231fSLeilk Liu {
571a568231fSLeilk Liu int mult_delta;
572a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
573a568231fSLeilk Liu
574a568231fSLeilk Liu if (mdata->tx_sgl_len && mdata->rx_sgl_len) {
575a568231fSLeilk Liu if (mdata->tx_sgl_len > mdata->rx_sgl_len) {
576cf82d0ecSzhichao.liu mult_delta = mtk_spi_get_mult_delta(mdata, mdata->rx_sgl_len);
577a568231fSLeilk Liu mdata->xfer_len = mdata->rx_sgl_len - mult_delta;
578a568231fSLeilk Liu mdata->rx_sgl_len = mult_delta;
579a568231fSLeilk Liu mdata->tx_sgl_len -= mdata->xfer_len;
580a568231fSLeilk Liu } else {
581cf82d0ecSzhichao.liu mult_delta = mtk_spi_get_mult_delta(mdata, mdata->tx_sgl_len);
582a568231fSLeilk Liu mdata->xfer_len = mdata->tx_sgl_len - mult_delta;
583a568231fSLeilk Liu mdata->tx_sgl_len = mult_delta;
584a568231fSLeilk Liu mdata->rx_sgl_len -= mdata->xfer_len;
585a568231fSLeilk Liu }
586a568231fSLeilk Liu } else if (mdata->tx_sgl_len) {
587cf82d0ecSzhichao.liu mult_delta = mtk_spi_get_mult_delta(mdata, mdata->tx_sgl_len);
588a568231fSLeilk Liu mdata->xfer_len = mdata->tx_sgl_len - mult_delta;
589a568231fSLeilk Liu mdata->tx_sgl_len = mult_delta;
590a568231fSLeilk Liu } else if (mdata->rx_sgl_len) {
591cf82d0ecSzhichao.liu mult_delta = mtk_spi_get_mult_delta(mdata, mdata->rx_sgl_len);
592a568231fSLeilk Liu mdata->xfer_len = mdata->rx_sgl_len - mult_delta;
593a568231fSLeilk Liu mdata->rx_sgl_len = mult_delta;
594a568231fSLeilk Liu }
595a568231fSLeilk Liu }
596a568231fSLeilk Liu
mtk_spi_setup_dma_addr(struct spi_master * master,struct spi_transfer * xfer)597a568231fSLeilk Liu static void mtk_spi_setup_dma_addr(struct spi_master *master,
598a568231fSLeilk Liu struct spi_transfer *xfer)
599a568231fSLeilk Liu {
600a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
601a568231fSLeilk Liu
602fdeae8f5Sluhua.xu if (mdata->tx_sgl) {
603fdeae8f5Sluhua.xu writel((u32)(xfer->tx_dma & MTK_SPI_32BITS_MASK),
604fdeae8f5Sluhua.xu mdata->base + SPI_TX_SRC_REG);
605fdeae8f5Sluhua.xu #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
606fdeae8f5Sluhua.xu if (mdata->dev_comp->dma_ext)
607fdeae8f5Sluhua.xu writel((u32)(xfer->tx_dma >> 32),
608fdeae8f5Sluhua.xu mdata->base + SPI_TX_SRC_REG_64);
609fdeae8f5Sluhua.xu #endif
610fdeae8f5Sluhua.xu }
611fdeae8f5Sluhua.xu
612fdeae8f5Sluhua.xu if (mdata->rx_sgl) {
613fdeae8f5Sluhua.xu writel((u32)(xfer->rx_dma & MTK_SPI_32BITS_MASK),
614fdeae8f5Sluhua.xu mdata->base + SPI_RX_DST_REG);
615fdeae8f5Sluhua.xu #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
616fdeae8f5Sluhua.xu if (mdata->dev_comp->dma_ext)
617fdeae8f5Sluhua.xu writel((u32)(xfer->rx_dma >> 32),
618fdeae8f5Sluhua.xu mdata->base + SPI_RX_DST_REG_64);
619fdeae8f5Sluhua.xu #endif
620fdeae8f5Sluhua.xu }
621a568231fSLeilk Liu }
622a568231fSLeilk Liu
mtk_spi_fifo_transfer(struct spi_master * master,struct spi_device * spi,struct spi_transfer * xfer)623a568231fSLeilk Liu static int mtk_spi_fifo_transfer(struct spi_master *master,
624a568231fSLeilk Liu struct spi_device *spi,
625a568231fSLeilk Liu struct spi_transfer *xfer)
626a568231fSLeilk Liu {
627de327e49SNicolas Boichat int cnt, remainder;
628de327e49SNicolas Boichat u32 reg_val;
629a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
630a568231fSLeilk Liu
631a568231fSLeilk Liu mdata->cur_transfer = xfer;
6321ce24864SDaniel Kurtz mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len);
63300bca73bSPeter Shih mdata->num_xfered = 0;
6347e963fb2SLeilk Liu mtk_spi_prepare_transfer(master, xfer->speed_hz);
635a568231fSLeilk Liu mtk_spi_setup_packet(master);
636a568231fSLeilk Liu
6370d5c3954SGuenter Roeck if (xfer->tx_buf) {
638a568231fSLeilk Liu cnt = xfer->len / 4;
63944f636daSLeilk Liu iowrite32_rep(mdata->base + SPI_TX_DATA_REG, xfer->tx_buf, cnt);
640de327e49SNicolas Boichat remainder = xfer->len % 4;
641de327e49SNicolas Boichat if (remainder > 0) {
642de327e49SNicolas Boichat reg_val = 0;
643de327e49SNicolas Boichat memcpy(®_val, xfer->tx_buf + (cnt * 4), remainder);
644de327e49SNicolas Boichat writel(reg_val, mdata->base + SPI_TX_DATA_REG);
645de327e49SNicolas Boichat }
6463a70dd2dSPeter Hess }
647de327e49SNicolas Boichat
648a568231fSLeilk Liu mtk_spi_enable_transfer(master);
649a568231fSLeilk Liu
650a568231fSLeilk Liu return 1;
651a568231fSLeilk Liu }
652a568231fSLeilk Liu
mtk_spi_dma_transfer(struct spi_master * master,struct spi_device * spi,struct spi_transfer * xfer)653a568231fSLeilk Liu static int mtk_spi_dma_transfer(struct spi_master *master,
654a568231fSLeilk Liu struct spi_device *spi,
655a568231fSLeilk Liu struct spi_transfer *xfer)
656a568231fSLeilk Liu {
657a568231fSLeilk Liu int cmd;
658a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
659a568231fSLeilk Liu
660a568231fSLeilk Liu mdata->tx_sgl = NULL;
661a568231fSLeilk Liu mdata->rx_sgl = NULL;
662a568231fSLeilk Liu mdata->tx_sgl_len = 0;
663a568231fSLeilk Liu mdata->rx_sgl_len = 0;
664a568231fSLeilk Liu mdata->cur_transfer = xfer;
66500bca73bSPeter Shih mdata->num_xfered = 0;
666a568231fSLeilk Liu
6677e963fb2SLeilk Liu mtk_spi_prepare_transfer(master, xfer->speed_hz);
668a568231fSLeilk Liu
669a568231fSLeilk Liu cmd = readl(mdata->base + SPI_CMD_REG);
670a568231fSLeilk Liu if (xfer->tx_buf)
671a568231fSLeilk Liu cmd |= SPI_CMD_TX_DMA;
672a568231fSLeilk Liu if (xfer->rx_buf)
673a568231fSLeilk Liu cmd |= SPI_CMD_RX_DMA;
674a568231fSLeilk Liu writel(cmd, mdata->base + SPI_CMD_REG);
675a568231fSLeilk Liu
676a568231fSLeilk Liu if (xfer->tx_buf)
677a568231fSLeilk Liu mdata->tx_sgl = xfer->tx_sg.sgl;
678a568231fSLeilk Liu if (xfer->rx_buf)
679a568231fSLeilk Liu mdata->rx_sgl = xfer->rx_sg.sgl;
680a568231fSLeilk Liu
681a568231fSLeilk Liu if (mdata->tx_sgl) {
682a568231fSLeilk Liu xfer->tx_dma = sg_dma_address(mdata->tx_sgl);
683a568231fSLeilk Liu mdata->tx_sgl_len = sg_dma_len(mdata->tx_sgl);
684a568231fSLeilk Liu }
685a568231fSLeilk Liu if (mdata->rx_sgl) {
686a568231fSLeilk Liu xfer->rx_dma = sg_dma_address(mdata->rx_sgl);
687a568231fSLeilk Liu mdata->rx_sgl_len = sg_dma_len(mdata->rx_sgl);
688a568231fSLeilk Liu }
689a568231fSLeilk Liu
690a568231fSLeilk Liu mtk_spi_update_mdata_len(master);
691a568231fSLeilk Liu mtk_spi_setup_packet(master);
692a568231fSLeilk Liu mtk_spi_setup_dma_addr(master, xfer);
693a568231fSLeilk Liu mtk_spi_enable_transfer(master);
694a568231fSLeilk Liu
695a568231fSLeilk Liu return 1;
696a568231fSLeilk Liu }
697a568231fSLeilk Liu
mtk_spi_transfer_one(struct spi_master * master,struct spi_device * spi,struct spi_transfer * xfer)698a568231fSLeilk Liu static int mtk_spi_transfer_one(struct spi_master *master,
699a568231fSLeilk Liu struct spi_device *spi,
700a568231fSLeilk Liu struct spi_transfer *xfer)
701a568231fSLeilk Liu {
7027e963fb2SLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
7037e963fb2SLeilk Liu u32 reg_val = 0;
7047e963fb2SLeilk Liu
7057e963fb2SLeilk Liu /* prepare xfer direction and duplex mode */
7067e963fb2SLeilk Liu if (mdata->dev_comp->ipm_design) {
7077e963fb2SLeilk Liu if (!xfer->tx_buf || !xfer->rx_buf) {
7087e963fb2SLeilk Liu reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN;
7097e963fb2SLeilk Liu if (xfer->rx_buf)
7107e963fb2SLeilk Liu reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR;
7117e963fb2SLeilk Liu }
7127e963fb2SLeilk Liu writel(reg_val, mdata->base + SPI_CFG3_IPM_REG);
7137e963fb2SLeilk Liu }
7147e963fb2SLeilk Liu
715a568231fSLeilk Liu if (master->can_dma(master, spi, xfer))
716a568231fSLeilk Liu return mtk_spi_dma_transfer(master, spi, xfer);
717a568231fSLeilk Liu else
718a568231fSLeilk Liu return mtk_spi_fifo_transfer(master, spi, xfer);
719a568231fSLeilk Liu }
720a568231fSLeilk Liu
mtk_spi_can_dma(struct spi_master * master,struct spi_device * spi,struct spi_transfer * xfer)721a568231fSLeilk Liu static bool mtk_spi_can_dma(struct spi_master *master,
722a568231fSLeilk Liu struct spi_device *spi,
723a568231fSLeilk Liu struct spi_transfer *xfer)
724a568231fSLeilk Liu {
7251ce24864SDaniel Kurtz /* Buffers for DMA transactions must be 4-byte aligned */
7261ce24864SDaniel Kurtz return (xfer->len > MTK_SPI_MAX_FIFO_SIZE &&
7271ce24864SDaniel Kurtz (unsigned long)xfer->tx_buf % 4 == 0 &&
7281ce24864SDaniel Kurtz (unsigned long)xfer->rx_buf % 4 == 0);
729a568231fSLeilk Liu }
730a568231fSLeilk Liu
mtk_spi_setup(struct spi_device * spi)73158a984c7SLeilk Liu static int mtk_spi_setup(struct spi_device *spi)
73258a984c7SLeilk Liu {
73358a984c7SLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
73458a984c7SLeilk Liu
73558a984c7SLeilk Liu if (!spi->controller_data)
73658a984c7SLeilk Liu spi->controller_data = (void *)&mtk_default_chip_info;
73758a984c7SLeilk Liu
7389e264f3fSAmit Kumar Mahapatra via Alsa-devel if (mdata->dev_comp->need_pad_sel && spi_get_csgpiod(spi, 0))
7391a5a87d5SLinus Walleij /* CS de-asserted, gpiolib will handle inversion */
7409e264f3fSAmit Kumar Mahapatra via Alsa-devel gpiod_direction_output(spi_get_csgpiod(spi, 0), 0);
74137457607SLeilk Liu
74258a984c7SLeilk Liu return 0;
74358a984c7SLeilk Liu }
74458a984c7SLeilk Liu
mtk_spi_interrupt(int irq,void * dev_id)745a568231fSLeilk Liu static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
746a568231fSLeilk Liu {
74700bca73bSPeter Shih u32 cmd, reg_val, cnt, remainder, len;
748a568231fSLeilk Liu struct spi_master *master = dev_id;
749a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
750a568231fSLeilk Liu struct spi_transfer *trans = mdata->cur_transfer;
751a568231fSLeilk Liu
752a568231fSLeilk Liu reg_val = readl(mdata->base + SPI_STATUS0_REG);
75350f8fec2SLeilk Liu if (reg_val & MTK_SPI_PAUSE_INT_STATUS)
754a568231fSLeilk Liu mdata->state = MTK_SPI_PAUSED;
755a568231fSLeilk Liu else
756a568231fSLeilk Liu mdata->state = MTK_SPI_IDLE;
757a568231fSLeilk Liu
7589f763fd2SLeilk Liu /* SPI-MEM ops */
7599f763fd2SLeilk Liu if (mdata->use_spimem) {
7609f763fd2SLeilk Liu complete(&mdata->spimem_done);
7619f763fd2SLeilk Liu return IRQ_HANDLED;
7629f763fd2SLeilk Liu }
7639f763fd2SLeilk Liu
764f83a96e5SBenjamin Gaignard if (!master->can_dma(master, NULL, trans)) {
765a568231fSLeilk Liu if (trans->rx_buf) {
76644f636daSLeilk Liu cnt = mdata->xfer_len / 4;
76744f636daSLeilk Liu ioread32_rep(mdata->base + SPI_RX_DATA_REG,
76800bca73bSPeter Shih trans->rx_buf + mdata->num_xfered, cnt);
769de327e49SNicolas Boichat remainder = mdata->xfer_len % 4;
770de327e49SNicolas Boichat if (remainder > 0) {
771de327e49SNicolas Boichat reg_val = readl(mdata->base + SPI_RX_DATA_REG);
77200bca73bSPeter Shih memcpy(trans->rx_buf +
77300bca73bSPeter Shih mdata->num_xfered +
77400bca73bSPeter Shih (cnt * 4),
77500bca73bSPeter Shih ®_val,
77600bca73bSPeter Shih remainder);
777de327e49SNicolas Boichat }
778a568231fSLeilk Liu }
7791ce24864SDaniel Kurtz
78000bca73bSPeter Shih mdata->num_xfered += mdata->xfer_len;
78100bca73bSPeter Shih if (mdata->num_xfered == trans->len) {
782a568231fSLeilk Liu spi_finalize_current_transfer(master);
783a568231fSLeilk Liu return IRQ_HANDLED;
784a568231fSLeilk Liu }
785a568231fSLeilk Liu
78600bca73bSPeter Shih len = trans->len - mdata->num_xfered;
78700bca73bSPeter Shih mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, len);
7881ce24864SDaniel Kurtz mtk_spi_setup_packet(master);
7891ce24864SDaniel Kurtz
790*62b1f837SFei Shao if (trans->tx_buf) {
791a4d8f64fSLeilk Liu cnt = mdata->xfer_len / 4;
79200bca73bSPeter Shih iowrite32_rep(mdata->base + SPI_TX_DATA_REG,
79300bca73bSPeter Shih trans->tx_buf + mdata->num_xfered, cnt);
7941ce24864SDaniel Kurtz
795a4d8f64fSLeilk Liu remainder = mdata->xfer_len % 4;
7961ce24864SDaniel Kurtz if (remainder > 0) {
7971ce24864SDaniel Kurtz reg_val = 0;
79800bca73bSPeter Shih memcpy(®_val,
79900bca73bSPeter Shih trans->tx_buf + (cnt * 4) + mdata->num_xfered,
80000bca73bSPeter Shih remainder);
8011ce24864SDaniel Kurtz writel(reg_val, mdata->base + SPI_TX_DATA_REG);
8021ce24864SDaniel Kurtz }
803*62b1f837SFei Shao }
8041ce24864SDaniel Kurtz
8051ce24864SDaniel Kurtz mtk_spi_enable_transfer(master);
8061ce24864SDaniel Kurtz
8071ce24864SDaniel Kurtz return IRQ_HANDLED;
8081ce24864SDaniel Kurtz }
8091ce24864SDaniel Kurtz
810a568231fSLeilk Liu if (mdata->tx_sgl)
811a568231fSLeilk Liu trans->tx_dma += mdata->xfer_len;
812a568231fSLeilk Liu if (mdata->rx_sgl)
813a568231fSLeilk Liu trans->rx_dma += mdata->xfer_len;
814a568231fSLeilk Liu
815a568231fSLeilk Liu if (mdata->tx_sgl && (mdata->tx_sgl_len == 0)) {
816a568231fSLeilk Liu mdata->tx_sgl = sg_next(mdata->tx_sgl);
817a568231fSLeilk Liu if (mdata->tx_sgl) {
818a568231fSLeilk Liu trans->tx_dma = sg_dma_address(mdata->tx_sgl);
819a568231fSLeilk Liu mdata->tx_sgl_len = sg_dma_len(mdata->tx_sgl);
820a568231fSLeilk Liu }
821a568231fSLeilk Liu }
822a568231fSLeilk Liu if (mdata->rx_sgl && (mdata->rx_sgl_len == 0)) {
823a568231fSLeilk Liu mdata->rx_sgl = sg_next(mdata->rx_sgl);
824a568231fSLeilk Liu if (mdata->rx_sgl) {
825a568231fSLeilk Liu trans->rx_dma = sg_dma_address(mdata->rx_sgl);
826a568231fSLeilk Liu mdata->rx_sgl_len = sg_dma_len(mdata->rx_sgl);
827a568231fSLeilk Liu }
828a568231fSLeilk Liu }
829a568231fSLeilk Liu
830a568231fSLeilk Liu if (!mdata->tx_sgl && !mdata->rx_sgl) {
831a568231fSLeilk Liu /* spi disable dma */
832a568231fSLeilk Liu cmd = readl(mdata->base + SPI_CMD_REG);
833a568231fSLeilk Liu cmd &= ~SPI_CMD_TX_DMA;
834a568231fSLeilk Liu cmd &= ~SPI_CMD_RX_DMA;
835a568231fSLeilk Liu writel(cmd, mdata->base + SPI_CMD_REG);
836a568231fSLeilk Liu
837a568231fSLeilk Liu spi_finalize_current_transfer(master);
838a568231fSLeilk Liu return IRQ_HANDLED;
839a568231fSLeilk Liu }
840a568231fSLeilk Liu
841a568231fSLeilk Liu mtk_spi_update_mdata_len(master);
842a568231fSLeilk Liu mtk_spi_setup_packet(master);
843a568231fSLeilk Liu mtk_spi_setup_dma_addr(master, trans);
844a568231fSLeilk Liu mtk_spi_enable_transfer(master);
845a568231fSLeilk Liu
846a568231fSLeilk Liu return IRQ_HANDLED;
847a568231fSLeilk Liu }
848a568231fSLeilk Liu
mtk_spi_mem_adjust_op_size(struct spi_mem * mem,struct spi_mem_op * op)8499f763fd2SLeilk Liu static int mtk_spi_mem_adjust_op_size(struct spi_mem *mem,
8509f763fd2SLeilk Liu struct spi_mem_op *op)
8519f763fd2SLeilk Liu {
8529f763fd2SLeilk Liu int opcode_len;
8539f763fd2SLeilk Liu
8549f763fd2SLeilk Liu if (op->data.dir != SPI_MEM_NO_DATA) {
8559f763fd2SLeilk Liu opcode_len = 1 + op->addr.nbytes + op->dummy.nbytes;
8569f763fd2SLeilk Liu if (opcode_len + op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) {
8579f763fd2SLeilk Liu op->data.nbytes = MTK_SPI_IPM_PACKET_SIZE - opcode_len;
8589f763fd2SLeilk Liu /* force data buffer dma-aligned. */
8599f763fd2SLeilk Liu op->data.nbytes -= op->data.nbytes % 4;
8609f763fd2SLeilk Liu }
8619f763fd2SLeilk Liu }
8629f763fd2SLeilk Liu
8639f763fd2SLeilk Liu return 0;
8649f763fd2SLeilk Liu }
8659f763fd2SLeilk Liu
mtk_spi_mem_supports_op(struct spi_mem * mem,const struct spi_mem_op * op)8669f763fd2SLeilk Liu static bool mtk_spi_mem_supports_op(struct spi_mem *mem,
8679f763fd2SLeilk Liu const struct spi_mem_op *op)
8689f763fd2SLeilk Liu {
8699f763fd2SLeilk Liu if (!spi_mem_default_supports_op(mem, op))
8709f763fd2SLeilk Liu return false;
8719f763fd2SLeilk Liu
8729f763fd2SLeilk Liu if (op->addr.nbytes && op->dummy.nbytes &&
8739f763fd2SLeilk Liu op->addr.buswidth != op->dummy.buswidth)
8749f763fd2SLeilk Liu return false;
8759f763fd2SLeilk Liu
8769f763fd2SLeilk Liu if (op->addr.nbytes + op->dummy.nbytes > 16)
8779f763fd2SLeilk Liu return false;
8789f763fd2SLeilk Liu
8799f763fd2SLeilk Liu if (op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) {
8809f763fd2SLeilk Liu if (op->data.nbytes / MTK_SPI_IPM_PACKET_SIZE >
8819f763fd2SLeilk Liu MTK_SPI_IPM_PACKET_LOOP ||
8829f763fd2SLeilk Liu op->data.nbytes % MTK_SPI_IPM_PACKET_SIZE != 0)
8839f763fd2SLeilk Liu return false;
8849f763fd2SLeilk Liu }
8859f763fd2SLeilk Liu
8869f763fd2SLeilk Liu return true;
8879f763fd2SLeilk Liu }
8889f763fd2SLeilk Liu
mtk_spi_mem_setup_dma_xfer(struct spi_master * master,const struct spi_mem_op * op)8899f763fd2SLeilk Liu static void mtk_spi_mem_setup_dma_xfer(struct spi_master *master,
8909f763fd2SLeilk Liu const struct spi_mem_op *op)
8919f763fd2SLeilk Liu {
8929f763fd2SLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
8939f763fd2SLeilk Liu
8949f763fd2SLeilk Liu writel((u32)(mdata->tx_dma & MTK_SPI_32BITS_MASK),
8959f763fd2SLeilk Liu mdata->base + SPI_TX_SRC_REG);
8969f763fd2SLeilk Liu #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
8979f763fd2SLeilk Liu if (mdata->dev_comp->dma_ext)
8989f763fd2SLeilk Liu writel((u32)(mdata->tx_dma >> 32),
8999f763fd2SLeilk Liu mdata->base + SPI_TX_SRC_REG_64);
9009f763fd2SLeilk Liu #endif
9019f763fd2SLeilk Liu
9029f763fd2SLeilk Liu if (op->data.dir == SPI_MEM_DATA_IN) {
9039f763fd2SLeilk Liu writel((u32)(mdata->rx_dma & MTK_SPI_32BITS_MASK),
9049f763fd2SLeilk Liu mdata->base + SPI_RX_DST_REG);
9059f763fd2SLeilk Liu #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
9069f763fd2SLeilk Liu if (mdata->dev_comp->dma_ext)
9079f763fd2SLeilk Liu writel((u32)(mdata->rx_dma >> 32),
9089f763fd2SLeilk Liu mdata->base + SPI_RX_DST_REG_64);
9099f763fd2SLeilk Liu #endif
9109f763fd2SLeilk Liu }
9119f763fd2SLeilk Liu }
9129f763fd2SLeilk Liu
mtk_spi_transfer_wait(struct spi_mem * mem,const struct spi_mem_op * op)9139f763fd2SLeilk Liu static int mtk_spi_transfer_wait(struct spi_mem *mem,
9149f763fd2SLeilk Liu const struct spi_mem_op *op)
9159f763fd2SLeilk Liu {
9169f763fd2SLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master);
9179f763fd2SLeilk Liu /*
9189f763fd2SLeilk Liu * For each byte we wait for 8 cycles of the SPI clock.
9199f763fd2SLeilk Liu * Since speed is defined in Hz and we want milliseconds,
9209f763fd2SLeilk Liu * so it should be 8 * 1000.
9219f763fd2SLeilk Liu */
9229f763fd2SLeilk Liu u64 ms = 8000LL;
9239f763fd2SLeilk Liu
9249f763fd2SLeilk Liu if (op->data.dir == SPI_MEM_NO_DATA)
9259f763fd2SLeilk Liu ms *= 32; /* prevent we may get 0 for short transfers. */
9269f763fd2SLeilk Liu else
9279f763fd2SLeilk Liu ms *= op->data.nbytes;
9289f763fd2SLeilk Liu ms = div_u64(ms, mem->spi->max_speed_hz);
9299f763fd2SLeilk Liu ms += ms + 1000; /* 1s tolerance */
9309f763fd2SLeilk Liu
9319f763fd2SLeilk Liu if (ms > UINT_MAX)
9329f763fd2SLeilk Liu ms = UINT_MAX;
9339f763fd2SLeilk Liu
9349f763fd2SLeilk Liu if (!wait_for_completion_timeout(&mdata->spimem_done,
9359f763fd2SLeilk Liu msecs_to_jiffies(ms))) {
9369f763fd2SLeilk Liu dev_err(mdata->dev, "spi-mem transfer timeout\n");
9379f763fd2SLeilk Liu return -ETIMEDOUT;
9389f763fd2SLeilk Liu }
9399f763fd2SLeilk Liu
9409f763fd2SLeilk Liu return 0;
9419f763fd2SLeilk Liu }
9429f763fd2SLeilk Liu
mtk_spi_mem_exec_op(struct spi_mem * mem,const struct spi_mem_op * op)9439f763fd2SLeilk Liu static int mtk_spi_mem_exec_op(struct spi_mem *mem,
9449f763fd2SLeilk Liu const struct spi_mem_op *op)
9459f763fd2SLeilk Liu {
9469f763fd2SLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master);
9479f763fd2SLeilk Liu u32 reg_val, nio, tx_size;
9489f763fd2SLeilk Liu char *tx_tmp_buf, *rx_tmp_buf;
9499f763fd2SLeilk Liu int ret = 0;
9509f763fd2SLeilk Liu
9519f763fd2SLeilk Liu mdata->use_spimem = true;
9529f763fd2SLeilk Liu reinit_completion(&mdata->spimem_done);
9539f763fd2SLeilk Liu
9549f763fd2SLeilk Liu mtk_spi_reset(mdata);
9559f763fd2SLeilk Liu mtk_spi_hw_init(mem->spi->master, mem->spi);
9569f763fd2SLeilk Liu mtk_spi_prepare_transfer(mem->spi->master, mem->spi->max_speed_hz);
9579f763fd2SLeilk Liu
9589f763fd2SLeilk Liu reg_val = readl(mdata->base + SPI_CFG3_IPM_REG);
9599f763fd2SLeilk Liu /* opcode byte len */
9609f763fd2SLeilk Liu reg_val &= ~SPI_CFG3_IPM_CMD_BYTELEN_MASK;
9619f763fd2SLeilk Liu reg_val |= 1 << SPI_CFG3_IPM_CMD_BYTELEN_OFFSET;
9629f763fd2SLeilk Liu
9639f763fd2SLeilk Liu /* addr & dummy byte len */
9649f763fd2SLeilk Liu reg_val &= ~SPI_CFG3_IPM_ADDR_BYTELEN_MASK;
9659f763fd2SLeilk Liu if (op->addr.nbytes || op->dummy.nbytes)
9669f763fd2SLeilk Liu reg_val |= (op->addr.nbytes + op->dummy.nbytes) <<
9679f763fd2SLeilk Liu SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET;
9689f763fd2SLeilk Liu
9699f763fd2SLeilk Liu /* data byte len */
9709f763fd2SLeilk Liu if (op->data.dir == SPI_MEM_NO_DATA) {
9719f763fd2SLeilk Liu reg_val |= SPI_CFG3_IPM_NODATA_FLAG;
9729f763fd2SLeilk Liu writel(0, mdata->base + SPI_CFG1_REG);
9739f763fd2SLeilk Liu } else {
9749f763fd2SLeilk Liu reg_val &= ~SPI_CFG3_IPM_NODATA_FLAG;
9759f763fd2SLeilk Liu mdata->xfer_len = op->data.nbytes;
9769f763fd2SLeilk Liu mtk_spi_setup_packet(mem->spi->master);
9779f763fd2SLeilk Liu }
9789f763fd2SLeilk Liu
9799f763fd2SLeilk Liu if (op->addr.nbytes || op->dummy.nbytes) {
9809f763fd2SLeilk Liu if (op->addr.buswidth == 1 || op->dummy.buswidth == 1)
9819f763fd2SLeilk Liu reg_val |= SPI_CFG3_IPM_XMODE_EN;
9829f763fd2SLeilk Liu else
9839f763fd2SLeilk Liu reg_val &= ~SPI_CFG3_IPM_XMODE_EN;
9849f763fd2SLeilk Liu }
9859f763fd2SLeilk Liu
9869f763fd2SLeilk Liu if (op->addr.buswidth == 2 ||
9879f763fd2SLeilk Liu op->dummy.buswidth == 2 ||
9889f763fd2SLeilk Liu op->data.buswidth == 2)
9899f763fd2SLeilk Liu nio = 2;
9909f763fd2SLeilk Liu else if (op->addr.buswidth == 4 ||
9919f763fd2SLeilk Liu op->dummy.buswidth == 4 ||
9929f763fd2SLeilk Liu op->data.buswidth == 4)
9939f763fd2SLeilk Liu nio = 4;
9949f763fd2SLeilk Liu else
9959f763fd2SLeilk Liu nio = 1;
9969f763fd2SLeilk Liu
9979f763fd2SLeilk Liu reg_val &= ~SPI_CFG3_IPM_CMD_PIN_MODE_MASK;
9989f763fd2SLeilk Liu reg_val |= PIN_MODE_CFG(nio);
9999f763fd2SLeilk Liu
10009f763fd2SLeilk Liu reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN;
10019f763fd2SLeilk Liu if (op->data.dir == SPI_MEM_DATA_IN)
10029f763fd2SLeilk Liu reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR;
10039f763fd2SLeilk Liu else
10049f763fd2SLeilk Liu reg_val &= ~SPI_CFG3_IPM_HALF_DUPLEX_DIR;
10059f763fd2SLeilk Liu writel(reg_val, mdata->base + SPI_CFG3_IPM_REG);
10069f763fd2SLeilk Liu
10079f763fd2SLeilk Liu tx_size = 1 + op->addr.nbytes + op->dummy.nbytes;
10089f763fd2SLeilk Liu if (op->data.dir == SPI_MEM_DATA_OUT)
10099f763fd2SLeilk Liu tx_size += op->data.nbytes;
10109f763fd2SLeilk Liu
10119f763fd2SLeilk Liu tx_size = max_t(u32, tx_size, 32);
10129f763fd2SLeilk Liu
10139f763fd2SLeilk Liu tx_tmp_buf = kzalloc(tx_size, GFP_KERNEL | GFP_DMA);
10149f763fd2SLeilk Liu if (!tx_tmp_buf) {
10159f763fd2SLeilk Liu mdata->use_spimem = false;
10169f763fd2SLeilk Liu return -ENOMEM;
10179f763fd2SLeilk Liu }
10189f763fd2SLeilk Liu
10199f763fd2SLeilk Liu tx_tmp_buf[0] = op->cmd.opcode;
10209f763fd2SLeilk Liu
10219f763fd2SLeilk Liu if (op->addr.nbytes) {
10229f763fd2SLeilk Liu int i;
10239f763fd2SLeilk Liu
10249f763fd2SLeilk Liu for (i = 0; i < op->addr.nbytes; i++)
10259f763fd2SLeilk Liu tx_tmp_buf[i + 1] = op->addr.val >>
10269f763fd2SLeilk Liu (8 * (op->addr.nbytes - i - 1));
10279f763fd2SLeilk Liu }
10289f763fd2SLeilk Liu
10299f763fd2SLeilk Liu if (op->dummy.nbytes)
10309f763fd2SLeilk Liu memset(tx_tmp_buf + op->addr.nbytes + 1,
10319f763fd2SLeilk Liu 0xff,
10329f763fd2SLeilk Liu op->dummy.nbytes);
10339f763fd2SLeilk Liu
10349f763fd2SLeilk Liu if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
10359f763fd2SLeilk Liu memcpy(tx_tmp_buf + op->dummy.nbytes + op->addr.nbytes + 1,
10369f763fd2SLeilk Liu op->data.buf.out,
10379f763fd2SLeilk Liu op->data.nbytes);
10389f763fd2SLeilk Liu
10399f763fd2SLeilk Liu mdata->tx_dma = dma_map_single(mdata->dev, tx_tmp_buf,
10409f763fd2SLeilk Liu tx_size, DMA_TO_DEVICE);
10419f763fd2SLeilk Liu if (dma_mapping_error(mdata->dev, mdata->tx_dma)) {
10429f763fd2SLeilk Liu ret = -ENOMEM;
10439f763fd2SLeilk Liu goto err_exit;
10449f763fd2SLeilk Liu }
10459f763fd2SLeilk Liu
10469f763fd2SLeilk Liu if (op->data.dir == SPI_MEM_DATA_IN) {
10479f763fd2SLeilk Liu if (!IS_ALIGNED((size_t)op->data.buf.in, 4)) {
10489f763fd2SLeilk Liu rx_tmp_buf = kzalloc(op->data.nbytes,
10499f763fd2SLeilk Liu GFP_KERNEL | GFP_DMA);
10509f763fd2SLeilk Liu if (!rx_tmp_buf) {
10519f763fd2SLeilk Liu ret = -ENOMEM;
10529f763fd2SLeilk Liu goto unmap_tx_dma;
10539f763fd2SLeilk Liu }
10549f763fd2SLeilk Liu } else {
10559f763fd2SLeilk Liu rx_tmp_buf = op->data.buf.in;
10569f763fd2SLeilk Liu }
10579f763fd2SLeilk Liu
10589f763fd2SLeilk Liu mdata->rx_dma = dma_map_single(mdata->dev,
10599f763fd2SLeilk Liu rx_tmp_buf,
10609f763fd2SLeilk Liu op->data.nbytes,
10619f763fd2SLeilk Liu DMA_FROM_DEVICE);
10629f763fd2SLeilk Liu if (dma_mapping_error(mdata->dev, mdata->rx_dma)) {
10639f763fd2SLeilk Liu ret = -ENOMEM;
10649f763fd2SLeilk Liu goto kfree_rx_tmp_buf;
10659f763fd2SLeilk Liu }
10669f763fd2SLeilk Liu }
10679f763fd2SLeilk Liu
10689f763fd2SLeilk Liu reg_val = readl(mdata->base + SPI_CMD_REG);
10699f763fd2SLeilk Liu reg_val |= SPI_CMD_TX_DMA;
10709f763fd2SLeilk Liu if (op->data.dir == SPI_MEM_DATA_IN)
10719f763fd2SLeilk Liu reg_val |= SPI_CMD_RX_DMA;
10729f763fd2SLeilk Liu writel(reg_val, mdata->base + SPI_CMD_REG);
10739f763fd2SLeilk Liu
10749f763fd2SLeilk Liu mtk_spi_mem_setup_dma_xfer(mem->spi->master, op);
10759f763fd2SLeilk Liu
10769f763fd2SLeilk Liu mtk_spi_enable_transfer(mem->spi->master);
10779f763fd2SLeilk Liu
10789f763fd2SLeilk Liu /* Wait for the interrupt. */
10799f763fd2SLeilk Liu ret = mtk_spi_transfer_wait(mem, op);
10809f763fd2SLeilk Liu if (ret)
10819f763fd2SLeilk Liu goto unmap_rx_dma;
10829f763fd2SLeilk Liu
10839f763fd2SLeilk Liu /* spi disable dma */
10849f763fd2SLeilk Liu reg_val = readl(mdata->base + SPI_CMD_REG);
10859f763fd2SLeilk Liu reg_val &= ~SPI_CMD_TX_DMA;
10869f763fd2SLeilk Liu if (op->data.dir == SPI_MEM_DATA_IN)
10879f763fd2SLeilk Liu reg_val &= ~SPI_CMD_RX_DMA;
10889f763fd2SLeilk Liu writel(reg_val, mdata->base + SPI_CMD_REG);
10899f763fd2SLeilk Liu
10909f763fd2SLeilk Liu unmap_rx_dma:
10919f763fd2SLeilk Liu if (op->data.dir == SPI_MEM_DATA_IN) {
10929f763fd2SLeilk Liu dma_unmap_single(mdata->dev, mdata->rx_dma,
10939f763fd2SLeilk Liu op->data.nbytes, DMA_FROM_DEVICE);
10949f763fd2SLeilk Liu if (!IS_ALIGNED((size_t)op->data.buf.in, 4))
10959f763fd2SLeilk Liu memcpy(op->data.buf.in, rx_tmp_buf, op->data.nbytes);
10969f763fd2SLeilk Liu }
10979f763fd2SLeilk Liu kfree_rx_tmp_buf:
10989f763fd2SLeilk Liu if (op->data.dir == SPI_MEM_DATA_IN &&
10999f763fd2SLeilk Liu !IS_ALIGNED((size_t)op->data.buf.in, 4))
11009f763fd2SLeilk Liu kfree(rx_tmp_buf);
11019f763fd2SLeilk Liu unmap_tx_dma:
11029f763fd2SLeilk Liu dma_unmap_single(mdata->dev, mdata->tx_dma,
11039f763fd2SLeilk Liu tx_size, DMA_TO_DEVICE);
11049f763fd2SLeilk Liu err_exit:
11059f763fd2SLeilk Liu kfree(tx_tmp_buf);
11069f763fd2SLeilk Liu mdata->use_spimem = false;
11079f763fd2SLeilk Liu
11089f763fd2SLeilk Liu return ret;
11099f763fd2SLeilk Liu }
11109f763fd2SLeilk Liu
11119f763fd2SLeilk Liu static const struct spi_controller_mem_ops mtk_spi_mem_ops = {
11129f763fd2SLeilk Liu .adjust_op_size = mtk_spi_mem_adjust_op_size,
11139f763fd2SLeilk Liu .supports_op = mtk_spi_mem_supports_op,
11149f763fd2SLeilk Liu .exec_op = mtk_spi_mem_exec_op,
11159f763fd2SLeilk Liu };
11169f763fd2SLeilk Liu
mtk_spi_probe(struct platform_device * pdev)1117a568231fSLeilk Liu static int mtk_spi_probe(struct platform_device *pdev)
1118a568231fSLeilk Liu {
11196b444058SAngeloGioacchino Del Regno struct device *dev = &pdev->dev;
1120a568231fSLeilk Liu struct spi_master *master;
1121a568231fSLeilk Liu struct mtk_spi *mdata;
1122fdeae8f5Sluhua.xu int i, irq, ret, addr_bits;
1123a568231fSLeilk Liu
11246b444058SAngeloGioacchino Del Regno master = devm_spi_alloc_master(dev, sizeof(*mdata));
112520cdbb80SAngeloGioacchino Del Regno if (!master)
112620cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, -ENOMEM, "failed to alloc spi master\n");
1127a568231fSLeilk Liu
1128a568231fSLeilk Liu master->auto_runtime_pm = true;
11296b444058SAngeloGioacchino Del Regno master->dev.of_node = dev->of_node;
11303e582c6eSLeilk Liu master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
1131a568231fSLeilk Liu
1132a568231fSLeilk Liu master->set_cs = mtk_spi_set_cs;
1133a568231fSLeilk Liu master->prepare_message = mtk_spi_prepare_message;
1134a568231fSLeilk Liu master->transfer_one = mtk_spi_transfer_one;
1135a568231fSLeilk Liu master->can_dma = mtk_spi_can_dma;
113658a984c7SLeilk Liu master->setup = mtk_spi_setup;
11379f6e7e8dSleilk.liu master->set_cs_timing = mtk_spi_set_hw_cs_timing;
11381a5a87d5SLinus Walleij master->use_gpio_descriptors = true;
1139a568231fSLeilk Liu
1140a568231fSLeilk Liu mdata = spi_master_get_devdata(master);
11416b444058SAngeloGioacchino Del Regno mdata->dev_comp = device_get_match_data(dev);
1142ae7c2d34SLuhua Xu
1143ae7c2d34SLuhua Xu if (mdata->dev_comp->enhance_timing)
1144ae7c2d34SLuhua Xu master->mode_bits |= SPI_CS_HIGH;
1145ae7c2d34SLuhua Xu
1146a568231fSLeilk Liu if (mdata->dev_comp->must_tx)
114790366cd6SAndy Shevchenko master->flags = SPI_CONTROLLER_MUST_TX;
11487e963fb2SLeilk Liu if (mdata->dev_comp->ipm_design)
1149dcb2d274SQii Wang master->mode_bits |= SPI_LOOP | SPI_RX_DUAL | SPI_TX_DUAL |
1150dcb2d274SQii Wang SPI_RX_QUAD | SPI_TX_QUAD;
1151a568231fSLeilk Liu
11529f763fd2SLeilk Liu if (mdata->dev_comp->ipm_design) {
11536b444058SAngeloGioacchino Del Regno mdata->dev = dev;
11549f763fd2SLeilk Liu master->mem_ops = &mtk_spi_mem_ops;
11559f763fd2SLeilk Liu init_completion(&mdata->spimem_done);
11569f763fd2SLeilk Liu }
11579f763fd2SLeilk Liu
1158a568231fSLeilk Liu if (mdata->dev_comp->need_pad_sel) {
11596b444058SAngeloGioacchino Del Regno mdata->pad_num = of_property_count_u32_elems(dev->of_node,
116037457607SLeilk Liu "mediatek,pad-select");
116120cdbb80SAngeloGioacchino Del Regno if (mdata->pad_num < 0)
116220cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, -EINVAL,
116337457607SLeilk Liu "No 'mediatek,pad-select' property\n");
1164a568231fSLeilk Liu
11656b444058SAngeloGioacchino Del Regno mdata->pad_sel = devm_kmalloc_array(dev, mdata->pad_num,
116637457607SLeilk Liu sizeof(u32), GFP_KERNEL);
1167ace14580SAngeloGioacchino Del Regno if (!mdata->pad_sel)
1168ace14580SAngeloGioacchino Del Regno return -ENOMEM;
116937457607SLeilk Liu
117037457607SLeilk Liu for (i = 0; i < mdata->pad_num; i++) {
11716b444058SAngeloGioacchino Del Regno of_property_read_u32_index(dev->of_node,
117237457607SLeilk Liu "mediatek,pad-select",
117337457607SLeilk Liu i, &mdata->pad_sel[i]);
117420cdbb80SAngeloGioacchino Del Regno if (mdata->pad_sel[i] > MT8173_SPI_MAX_PAD_SEL)
117520cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, -EINVAL,
117620cdbb80SAngeloGioacchino Del Regno "wrong pad-sel[%d]: %u\n",
117737457607SLeilk Liu i, mdata->pad_sel[i]);
1178a568231fSLeilk Liu }
117937457607SLeilk Liu }
1180a568231fSLeilk Liu
1181a568231fSLeilk Liu platform_set_drvdata(pdev, master);
11825dd381e7SMarkus Elfring mdata->base = devm_platform_ioremap_resource(pdev, 0);
1183ace14580SAngeloGioacchino Del Regno if (IS_ERR(mdata->base))
1184ace14580SAngeloGioacchino Del Regno return PTR_ERR(mdata->base);
1185a568231fSLeilk Liu
1186a568231fSLeilk Liu irq = platform_get_irq(pdev, 0);
1187ace14580SAngeloGioacchino Del Regno if (irq < 0)
1188ace14580SAngeloGioacchino Del Regno return irq;
1189a568231fSLeilk Liu
11906b444058SAngeloGioacchino Del Regno if (!dev->dma_mask)
11916b444058SAngeloGioacchino Del Regno dev->dma_mask = &dev->coherent_dma_mask;
1192a568231fSLeilk Liu
1193309e9854Szhichao.liu if (mdata->dev_comp->ipm_design)
1194309e9854Szhichao.liu dma_set_max_seg_size(dev, SZ_16M);
1195309e9854Szhichao.liu else
1196309e9854Szhichao.liu dma_set_max_seg_size(dev, SZ_256K);
1197309e9854Szhichao.liu
11986b444058SAngeloGioacchino Del Regno mdata->parent_clk = devm_clk_get(dev, "parent-clk");
119920cdbb80SAngeloGioacchino Del Regno if (IS_ERR(mdata->parent_clk))
120020cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, PTR_ERR(mdata->parent_clk),
120120cdbb80SAngeloGioacchino Del Regno "failed to get parent-clk\n");
1202a568231fSLeilk Liu
12036b444058SAngeloGioacchino Del Regno mdata->sel_clk = devm_clk_get(dev, "sel-clk");
120420cdbb80SAngeloGioacchino Del Regno if (IS_ERR(mdata->sel_clk))
120520cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, PTR_ERR(mdata->sel_clk), "failed to get sel-clk\n");
1206adcbcfeaSLeilk Liu
12076b444058SAngeloGioacchino Del Regno mdata->spi_clk = devm_clk_get(dev, "spi-clk");
120820cdbb80SAngeloGioacchino Del Regno if (IS_ERR(mdata->spi_clk))
120920cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, PTR_ERR(mdata->spi_clk), "failed to get spi-clk\n");
1210adcbcfeaSLeilk Liu
12116b444058SAngeloGioacchino Del Regno mdata->spi_hclk = devm_clk_get_optional(dev, "hclk");
121220cdbb80SAngeloGioacchino Del Regno if (IS_ERR(mdata->spi_hclk))
121320cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, PTR_ERR(mdata->spi_hclk), "failed to get hclk\n");
1214a740f4e6SLeilk Liu
12155dee8bb8SAngeloGioacchino Del Regno ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk);
121620cdbb80SAngeloGioacchino Del Regno if (ret < 0)
121720cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, ret, "failed to clk_set_parent\n");
12185dee8bb8SAngeloGioacchino Del Regno
1219a740f4e6SLeilk Liu ret = clk_prepare_enable(mdata->spi_hclk);
122020cdbb80SAngeloGioacchino Del Regno if (ret < 0)
122120cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, ret, "failed to enable hclk\n");
1222a740f4e6SLeilk Liu
1223a568231fSLeilk Liu ret = clk_prepare_enable(mdata->spi_clk);
1224a568231fSLeilk Liu if (ret < 0) {
12255dee8bb8SAngeloGioacchino Del Regno clk_disable_unprepare(mdata->spi_hclk);
122620cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, ret, "failed to enable spi_clk\n");
1227a568231fSLeilk Liu }
1228a568231fSLeilk Liu
1229162a31efSMason Zhang mdata->spi_clk_hz = clk_get_rate(mdata->spi_clk);
1230162a31efSMason Zhang
1231a740f4e6SLeilk Liu if (mdata->dev_comp->no_need_unprepare) {
1232162a31efSMason Zhang clk_disable(mdata->spi_clk);
1233a740f4e6SLeilk Liu clk_disable(mdata->spi_hclk);
1234a740f4e6SLeilk Liu } else {
1235a568231fSLeilk Liu clk_disable_unprepare(mdata->spi_clk);
1236a740f4e6SLeilk Liu clk_disable_unprepare(mdata->spi_hclk);
1237a740f4e6SLeilk Liu }
1238a568231fSLeilk Liu
123937457607SLeilk Liu if (mdata->dev_comp->need_pad_sel) {
124020cdbb80SAngeloGioacchino Del Regno if (mdata->pad_num != master->num_chipselect)
124120cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, -EINVAL,
124237457607SLeilk Liu "pad_num does not match num_chipselect(%d != %d)\n",
124337457607SLeilk Liu mdata->pad_num, master->num_chipselect);
124437457607SLeilk Liu
124520cdbb80SAngeloGioacchino Del Regno if (!master->cs_gpiods && master->num_chipselect > 1)
124620cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, -EINVAL,
124798c8dccfSNicolas Boichat "cs_gpios not specified and num_chipselect > 1\n");
124898c8dccfSNicolas Boichat }
124937457607SLeilk Liu
1250fdeae8f5Sluhua.xu if (mdata->dev_comp->dma_ext)
1251fdeae8f5Sluhua.xu addr_bits = DMA_ADDR_EXT_BITS;
1252fdeae8f5Sluhua.xu else
1253fdeae8f5Sluhua.xu addr_bits = DMA_ADDR_DEF_BITS;
12546b444058SAngeloGioacchino Del Regno ret = dma_set_mask(dev, DMA_BIT_MASK(addr_bits));
1255fdeae8f5Sluhua.xu if (ret)
12566b444058SAngeloGioacchino Del Regno dev_notice(dev, "SPI dma_set_mask(%d) failed, ret:%d\n",
1257fdeae8f5Sluhua.xu addr_bits, ret);
1258fdeae8f5Sluhua.xu
1259b24cded8SRicardo Ribalda ret = devm_request_irq(dev, irq, mtk_spi_interrupt,
1260b24cded8SRicardo Ribalda IRQF_TRIGGER_NONE, dev_name(dev), master);
1261b24cded8SRicardo Ribalda if (ret)
1262b24cded8SRicardo Ribalda return dev_err_probe(dev, ret, "failed to register irq\n");
1263b24cded8SRicardo Ribalda
12645088b313SAngeloGioacchino Del Regno pm_runtime_enable(dev);
12655088b313SAngeloGioacchino Del Regno
12666b444058SAngeloGioacchino Del Regno ret = devm_spi_register_master(dev, master);
1267c934fec1SMason Zhang if (ret) {
12685088b313SAngeloGioacchino Del Regno pm_runtime_disable(dev);
126920cdbb80SAngeloGioacchino Del Regno return dev_err_probe(dev, ret, "failed to register master\n");
1270c934fec1SMason Zhang }
1271c934fec1SMason Zhang
1272a568231fSLeilk Liu return 0;
1273a568231fSLeilk Liu }
1274a568231fSLeilk Liu
mtk_spi_remove(struct platform_device * pdev)1275df7e4719SUwe Kleine-König static void mtk_spi_remove(struct platform_device *pdev)
1276a568231fSLeilk Liu {
1277a568231fSLeilk Liu struct spi_master *master = platform_get_drvdata(pdev);
1278a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
12790d10e90cSZhichao Liu int ret;
1280a568231fSLeilk Liu
12814be47a5dSDaniel Golle if (mdata->use_spimem && !completion_done(&mdata->spimem_done))
12824be47a5dSDaniel Golle complete(&mdata->spimem_done);
12834be47a5dSDaniel Golle
128422f40727SUwe Kleine-König ret = pm_runtime_get_sync(&pdev->dev);
128522f40727SUwe Kleine-König if (ret < 0) {
128622f40727SUwe Kleine-König dev_warn(&pdev->dev, "Failed to resume hardware (%pe)\n", ERR_PTR(ret));
128722f40727SUwe Kleine-König } else {
128822f40727SUwe Kleine-König /*
128922f40727SUwe Kleine-König * If pm runtime resume failed, clks are disabled and
129022f40727SUwe Kleine-König * unprepared. So don't access the hardware and skip clk
129122f40727SUwe Kleine-König * unpreparing.
129222f40727SUwe Kleine-König */
1293a568231fSLeilk Liu mtk_spi_reset(mdata);
1294a568231fSLeilk Liu
1295a740f4e6SLeilk Liu if (mdata->dev_comp->no_need_unprepare) {
1296162a31efSMason Zhang clk_unprepare(mdata->spi_clk);
1297a740f4e6SLeilk Liu clk_unprepare(mdata->spi_hclk);
1298a740f4e6SLeilk Liu }
129922f40727SUwe Kleine-König }
1300162a31efSMason Zhang
13010d10e90cSZhichao Liu pm_runtime_put_noidle(&pdev->dev);
13020d10e90cSZhichao Liu pm_runtime_disable(&pdev->dev);
1303a568231fSLeilk Liu }
1304a568231fSLeilk Liu
1305a568231fSLeilk Liu #ifdef CONFIG_PM_SLEEP
mtk_spi_suspend(struct device * dev)1306a568231fSLeilk Liu static int mtk_spi_suspend(struct device *dev)
1307a568231fSLeilk Liu {
1308a568231fSLeilk Liu int ret;
1309a568231fSLeilk Liu struct spi_master *master = dev_get_drvdata(dev);
1310a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
1311a568231fSLeilk Liu
1312a568231fSLeilk Liu ret = spi_master_suspend(master);
1313a568231fSLeilk Liu if (ret)
1314a568231fSLeilk Liu return ret;
1315a568231fSLeilk Liu
1316a740f4e6SLeilk Liu if (!pm_runtime_suspended(dev)) {
1317a568231fSLeilk Liu clk_disable_unprepare(mdata->spi_clk);
1318a740f4e6SLeilk Liu clk_disable_unprepare(mdata->spi_hclk);
1319a740f4e6SLeilk Liu }
1320a568231fSLeilk Liu
13216f089e98SUwe Kleine-König return 0;
1322a568231fSLeilk Liu }
1323a568231fSLeilk Liu
mtk_spi_resume(struct device * dev)1324a568231fSLeilk Liu static int mtk_spi_resume(struct device *dev)
1325a568231fSLeilk Liu {
1326a568231fSLeilk Liu int ret;
1327a568231fSLeilk Liu struct spi_master *master = dev_get_drvdata(dev);
1328a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
1329a568231fSLeilk Liu
1330a568231fSLeilk Liu if (!pm_runtime_suspended(dev)) {
1331a568231fSLeilk Liu ret = clk_prepare_enable(mdata->spi_clk);
133213da5a0bSLeilk Liu if (ret < 0) {
133313da5a0bSLeilk Liu dev_err(dev, "failed to enable spi_clk (%d)\n", ret);
1334a568231fSLeilk Liu return ret;
1335a568231fSLeilk Liu }
1336a740f4e6SLeilk Liu
1337a740f4e6SLeilk Liu ret = clk_prepare_enable(mdata->spi_hclk);
1338a740f4e6SLeilk Liu if (ret < 0) {
1339a740f4e6SLeilk Liu dev_err(dev, "failed to enable spi_hclk (%d)\n", ret);
1340a740f4e6SLeilk Liu clk_disable_unprepare(mdata->spi_clk);
1341a740f4e6SLeilk Liu return ret;
1342a740f4e6SLeilk Liu }
134313da5a0bSLeilk Liu }
1344a568231fSLeilk Liu
1345a568231fSLeilk Liu ret = spi_master_resume(master);
1346a740f4e6SLeilk Liu if (ret < 0) {
1347a568231fSLeilk Liu clk_disable_unprepare(mdata->spi_clk);
1348a740f4e6SLeilk Liu clk_disable_unprepare(mdata->spi_hclk);
1349a740f4e6SLeilk Liu }
1350a568231fSLeilk Liu
1351a568231fSLeilk Liu return ret;
1352a568231fSLeilk Liu }
1353a568231fSLeilk Liu #endif /* CONFIG_PM_SLEEP */
1354a568231fSLeilk Liu
1355a568231fSLeilk Liu #ifdef CONFIG_PM
mtk_spi_runtime_suspend(struct device * dev)1356a568231fSLeilk Liu static int mtk_spi_runtime_suspend(struct device *dev)
1357a568231fSLeilk Liu {
1358a568231fSLeilk Liu struct spi_master *master = dev_get_drvdata(dev);
1359a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
1360a568231fSLeilk Liu
1361a740f4e6SLeilk Liu if (mdata->dev_comp->no_need_unprepare) {
1362162a31efSMason Zhang clk_disable(mdata->spi_clk);
1363a740f4e6SLeilk Liu clk_disable(mdata->spi_hclk);
1364a740f4e6SLeilk Liu } else {
1365a568231fSLeilk Liu clk_disable_unprepare(mdata->spi_clk);
1366a740f4e6SLeilk Liu clk_disable_unprepare(mdata->spi_hclk);
1367a740f4e6SLeilk Liu }
1368a568231fSLeilk Liu
1369a568231fSLeilk Liu return 0;
1370a568231fSLeilk Liu }
1371a568231fSLeilk Liu
mtk_spi_runtime_resume(struct device * dev)1372a568231fSLeilk Liu static int mtk_spi_runtime_resume(struct device *dev)
1373a568231fSLeilk Liu {
1374a568231fSLeilk Liu struct spi_master *master = dev_get_drvdata(dev);
1375a568231fSLeilk Liu struct mtk_spi *mdata = spi_master_get_devdata(master);
137613da5a0bSLeilk Liu int ret;
1377a568231fSLeilk Liu
1378a740f4e6SLeilk Liu if (mdata->dev_comp->no_need_unprepare) {
1379162a31efSMason Zhang ret = clk_enable(mdata->spi_clk);
138013da5a0bSLeilk Liu if (ret < 0) {
138113da5a0bSLeilk Liu dev_err(dev, "failed to enable spi_clk (%d)\n", ret);
138213da5a0bSLeilk Liu return ret;
138313da5a0bSLeilk Liu }
1384a740f4e6SLeilk Liu ret = clk_enable(mdata->spi_hclk);
1385a740f4e6SLeilk Liu if (ret < 0) {
1386a740f4e6SLeilk Liu dev_err(dev, "failed to enable spi_hclk (%d)\n", ret);
1387a740f4e6SLeilk Liu clk_disable(mdata->spi_clk);
1388a740f4e6SLeilk Liu return ret;
1389a740f4e6SLeilk Liu }
1390a740f4e6SLeilk Liu } else {
1391a740f4e6SLeilk Liu ret = clk_prepare_enable(mdata->spi_clk);
1392a740f4e6SLeilk Liu if (ret < 0) {
1393a740f4e6SLeilk Liu dev_err(dev, "failed to prepare_enable spi_clk (%d)\n", ret);
1394a740f4e6SLeilk Liu return ret;
1395a740f4e6SLeilk Liu }
1396a740f4e6SLeilk Liu
1397a740f4e6SLeilk Liu ret = clk_prepare_enable(mdata->spi_hclk);
1398a740f4e6SLeilk Liu if (ret < 0) {
1399a740f4e6SLeilk Liu dev_err(dev, "failed to prepare_enable spi_hclk (%d)\n", ret);
1400a740f4e6SLeilk Liu clk_disable_unprepare(mdata->spi_clk);
1401a740f4e6SLeilk Liu return ret;
1402a740f4e6SLeilk Liu }
1403a740f4e6SLeilk Liu }
140413da5a0bSLeilk Liu
140513da5a0bSLeilk Liu return 0;
1406a568231fSLeilk Liu }
1407a568231fSLeilk Liu #endif /* CONFIG_PM */
1408a568231fSLeilk Liu
1409a568231fSLeilk Liu static const struct dev_pm_ops mtk_spi_pm = {
1410a568231fSLeilk Liu SET_SYSTEM_SLEEP_PM_OPS(mtk_spi_suspend, mtk_spi_resume)
1411a568231fSLeilk Liu SET_RUNTIME_PM_OPS(mtk_spi_runtime_suspend,
1412a568231fSLeilk Liu mtk_spi_runtime_resume, NULL)
1413a568231fSLeilk Liu };
1414a568231fSLeilk Liu
14154299aaaaSkbuild test robot static struct platform_driver mtk_spi_driver = {
1416a568231fSLeilk Liu .driver = {
1417a568231fSLeilk Liu .name = "mtk-spi",
1418a568231fSLeilk Liu .pm = &mtk_spi_pm,
1419a568231fSLeilk Liu .of_match_table = mtk_spi_of_match,
1420a568231fSLeilk Liu },
1421a568231fSLeilk Liu .probe = mtk_spi_probe,
1422df7e4719SUwe Kleine-König .remove_new = mtk_spi_remove,
1423a568231fSLeilk Liu };
1424a568231fSLeilk Liu
1425a568231fSLeilk Liu module_platform_driver(mtk_spi_driver);
1426a568231fSLeilk Liu
1427a568231fSLeilk Liu MODULE_DESCRIPTION("MTK SPI Controller driver");
1428a568231fSLeilk Liu MODULE_AUTHOR("Leilk Liu <leilk.liu@mediatek.com>");
1429a568231fSLeilk Liu MODULE_LICENSE("GPL v2");
1430e4001885SAxel Lin MODULE_ALIAS("platform:mtk-spi");
1431